Haskell: чем отличаются нестрогие и ленивые?

Я часто читаю, что лень - это не то же самое как нестрогом но мне трудно понять разницу. Они!--13-->кажется использоваться взаимозаменяемо, но я понимаю, что они имеют разные значения. Я был бы признателен за помощь в понимании разницы.

у меня есть несколько вопросов, которые разбросаны по этому сообщению. Я резюмирую эти вопросы в конце этого поста. У меня есть несколько примеров фрагментов, которые я не тестировал я представил их только как концепции. Я добавил цитаты, чтобы спасти вас от поиска их. Может быть, это поможет кому-то позже с тем же вопросом.

Non-Strict Def:

функция f называется строгим, если применяется к бесконечной expression, он также не завершается. Другими словами, F является строго если значение f bot равно |. Для большинства языков программирования, все функции строги. Но это не так Хаскель. Как простой например, рассмотрим const1, функцию константы 1, определяемую:

const1 x = 1

значение бота const1 в Haskell равно 1. Операционно говоря, так как const1 не" нуждается " в значении своего аргумента, он никогда не пытается оценить его, и таким образом не попадает в непрерывающие вычисление. По этой причине нестрогие функции также называются "ленивые функции", и, как говорят, оценивают свои аргументы "лениво", или "по необходимости".

-Нежное Введение В Haskell: Функции

мне очень нравится это определение. Кажется, это лучшее, что я мог найти для понимания strict. Is const1 x = 1 ленивый, а?

non-strictness означает, что сокращение (математический термин для оценка) исходит извне в,

Итак, если у вас есть (a+(bc)) затем сначала вы уменьшаете+, затем вы уменьшаете этот внутренний (бc).

-Haskell Wiki: Лави против нестрогих

Haskell Wiki действительно смущает меня. Я понимаю, что они говорят о порядке, но я не понимаю, как (a+(b*c)) будет оценивать не строго, если это был pass _|_?

в нестрогой оценке аргументы функции не оцениваются если они фактически не используются при оценке функционального тела.

Под Церковь кодирование, ленивая оценка отображений операторов в нестрогие оценка функций; по этой причине, не-строгая оценка часто называют "ленивым". Во многих языках используются логические выражения форма нестрогой оценки называется оценка короткого замыкания, где оценка возвращается, как только можно определить, что однозначный Boolean приведет - например, в дизъюнктивном выражении, где true встречается или в конъюнктивном выражении, где false встречались и так далее. Условные выражения также обычно используют ленивая оценка, где оценка возвращается, как только однозначная ответвление будет результатом.

-Википедия: Стратегия Оценки

Ленивый Def:

ленивая оценка, с другой стороны, означает только оценку выражение, когда его результаты необходимы (обратите внимание на сдвиг от "сокращение "до " оценка"). Поэтому, когда механизм оценки видит expression создает структуру данных thunk, содержащую любые значения необходимы для оценки выражения, плюс указатель на самовыражение. Когда результат действительно необходим, оценка engine вызывает выражение, а затем заменяет thunk на результат для дальнейшего использования. ...

очевидно, что существует сильное соответствие между thunk и a частично оцененное выражение. Следовательно, в большинстве случаев термины "ленивый" и "нестрогие" - синонимы. Но не совсем.

-Haskell Wiki: Лави против нестрогих

это похоже на конкретный ответ Haskell. Я беру это лень означает thunks и нестрогом подразумевает частичную оценку. Это сравнение слишком упрощено? Делает лень всегда означают удары и нестрогом всегда означает частичную оценку.

в теории языка программирования, ленив или call-by-need1 is стратегия оценки, которая задерживает оценку выражения пока его значение фактически не требуется (не строгая оценка) , а также избегайте повторных оценок (обмена).

-Википедия: Ленивая Оценка

Императив Примеры

Я знаю, что большинство людей говорят забыть императивное Программирование при изучении функционального языка. Однако я хотел бы знать, квалифицируются ли они как не строгий, ленивый, оба или ни одного? По крайней мере, что-то знакомое.

Короткое Замыкание

f1() || f2()

C#, Python и другие языки с "yield"

public static IEnumerable Power(int number, int exponent)
{
    int counter = 0;
    int result = 1;
    while (counter++ < exponent)
    {
        result = result * number;
        yield return result;
    }
}

-MSDN: выход (c#)

обратные вызовы

int f1() { return 1;}
int f2() { return 2;}

int lazy(int (*cb1)(), int (*cb2)() , int x) {
    if (x == 0)
        return cb1();
    else
        return cb2();
}

int eager(int e1, int e2, int x) {
    if (x == 0)
         return e1;
    else
         return e2;
}

lazy(f1, f2, x);
eager(f1(), f2(), x);

вопросы

Я знаю, что ответ прямо передо мной со всеми этими ресурсами, но я не могу понять его. Все это кажется, что определение слишком легко отбрасывается как подразумеваемое или очевидное.

Я знаю, что у меня много вопросов. Не стесняйтесь ответить на любые вопросы, которые вы считаете актуальными. Я добавил эти вопросы для обсуждения.

  • Is const1 x = 1 тоже ленивый?
  • как оценивать от "внутреннего" нестрогого? Это потому, что inward позволяет сокращения ненужных выражений, как в const1 x = 1? Сокращения, как представляется, соответствуют определению ленивый.
  • тут лень всегда означает thunks и нестрогом всегда означает частичное оценке? Это просто обобщение?
  • следующие императив концепции ленивый, нестрогое, оба или ни одного?
    • Короткое Замыкание
    • использование yield
    • передача обратных вызовов для задержки или предотвращения выполнения
  • Is лень подмножество нестрогом или наоборот, или они взаимоисключающие. Например, возможно ли быть нестрогом без лень или лень без нестрогом?
  • достигается ли не-строгость Хаскелла ленью?

спасибо!

5 ответов


нестрогие и ленивые, хотя неофициально взаимозаменяемые, применяются к различным областям обсуждения.

нестрогом относится к семантика: математическое значение выражения. Мир, к которому применяется нестрогий, не имеет понятия о времени работы функции, потреблении памяти или даже компьютере. Он просто рассказывает о том, какие виды значений в домене карте, какие ценности в Йодомарин. В частности, строго функция должна сопоставить значение ⊥ ("внизу" -- см. ссылку семантики выше для получения дополнительной информации об этом) с ⊥; нестрогая функция не может этого сделать.

лень относится к операционному поведению: способ выполнения кода на реальном компьютере. Большинство программистов думают о программах оперативно, так что это, вероятно, то, что вы думаете. Ленивая оценка относится к реализации с использованием thunks -- указателей на код, которые заменяются значением первым пора их казнить. Обратите внимание на несемантические слова здесь: "указатель", "первый раз", "выполнено".

ленивая оценка порождает нестрогую семантику, поэтому понятия кажутся настолько близкими друг к другу. Но, как указывает FUZxxl, лень-не единственный способ реализовать нестрогую семантику.

Если вам интересно узнать больше об этом различии, я настоятельно рекомендую ссылку выше. Чтение стало поворотным пунктом в моем понимании значения слова программы компьютера.


пример для модели оценки, то есть ни строго, ни ленивый: оптимистичные оценки, что дает некоторое ускорение, так как это может избежать много "легких" ударов:

оптимистическая оценка означает, что даже если подвыражение может не потребоваться для оценки сверхэкспрессии, мы все равно оцениваем некоторые из них с помощью некоторых эвристик. Если подвыражение не завершается достаточно быстро, мы приостанавливаем его оценку до тех пор, пока действительно нуждаться. Это дает нам преимущество перед ленивой оценкой, если подвыражение необходимо позже, так как нам не нужно генерировать thunk. С другой стороны, мы не теряем слишком много, если выражение не заканчивается, так как мы можем прервать его достаточно быстро.

Как вы можете видеть, эта модель оценки не строгий: Если что-то, что дает _|_ оценивается, но не требуется, функция все равно завершится, поскольку двигатель прерывает оценку. С другой стороны, возможно, будет оценено больше выражений, чем необходимо, поэтому это не полностью лень.


Да, здесь есть неясное использование терминологии, но термины совпадают в большинстве случаев независимо, поэтому это не слишком большая проблема.

одно главное отличие -когда условия оцениваются. Для этого существует несколько стратегий, начиная от "как можно скорее" до "только в последний момент". Термин нетерпеливой оценке иногда используется для стратегий, склоняющихся к первому, в то время как ленивый оценке правильно относится к семейству стратегий, сильно склоняющихся к последнему. Различие между "ленивой оценкой" и связанными с ней стратегиями, как правило, заключается в том, когда и где результат оценки сохраняется, а не отбрасывается. На этом основан знакомый метод запоминания в Haskell присвоения имени структуре данных и индексирования в нее. Напротив, язык, который просто соединял выражения друг с другом (как в оценке" call-by-name"), может не поддерживать этот.

другое различие -какие условия оцениваются, начиная от " абсолютно все "до"как можно меньше". Поскольку любое значение, фактически используемое для вычисления конечного результата, нельзя игнорировать, разница здесь заключается в том, сколько лишних терминов оценивается. Помимо сокращения объема работы, которую должна выполнять программа, игнорирование неиспользуемых условий означает, что любые ошибки, которые они породили бы, не возникнут. Когда различие рисуется, требовательность относится к свойству оценки всего рассматриваемого (в случае строгой функции, например, это означает термины, к которым она применяется. Это не обязательно означает под-выражения внутри аргументов), в то время как нестрогом означает оценку только некоторых вещей (либо задерживая оценку, либо полностью отбрасывая термины).

должно быть легко увидеть, как они взаимодействуют сложными способами; решения не являются вообще ортогональны, так как крайности, как правило, несовместимы. Например:

  • очень нестрогая оценка исключает некоторое количество рвения; если вы не знаете, нужен ли термин, вы не можете оценить его еще.

  • очень строгая оценка делает нетерпение несколько неуместным; если вы оцениваете все, решение , когда сделать это менее значительно.

альтернативный однако определения существуют. Например, по крайней мере, в Haskell "строгая функция" часто определяется как та, которая заставляет свои аргументы достаточно, чтобы функция оценивала | всякий раз, когда какой-либо аргумент делает; обратите внимание, что по этому определению, id является строгим (в тривиальном смысле), потому что форсирование результата id x будет иметь точно такое же поведение, как и принуждение .


Это началось как обновление, но оно начало удлиняться.

лень / Call-by-need - это memoized версия call-by-name, где, если вычисляется аргумент функции, это значение сохраняется для последующего использования. В" чистой " (без эффекта) настройке это дает те же результаты, что и call-by-name; когда аргумент функции используется два или более раз, call-by-need почти всегда быстрее.
Императив Пример - видимо это возможно. Есть интересная статья о Ленивые Императивные Языки. Он говорит, что есть два метода. Один требует закрытия, второй использует сокращения графика. Поскольку C не поддерживает замыкания, вам нужно будет явно передать аргумент вашему итератору. Вы можете обернуть структуру карты и, если значение не существует, вычислить его иначе возвращаемое значение.
Примечание: Haskell реализует это с помощью " указателей на код, которые заменяются значением первым раз они казнены " - лукви.
Это не строгий вызов по имени, но с обменом / запоминанием результатов.

Позывной - при оценке вызова по имени аргументы функции не оцениваются до вызова функции-скорее, они подставляются непосредственно в тело функции (с использованием подстановки, избегающей захвата), а затем оставляются для оценки всякий раз, когда они появляются в функции. Если аргумент не используется в тело функции, аргумент никогда не оценивается; если он используется несколько раз, он повторно оценивается каждый раз, когда он появляется.
Императив Пример: обратные вызовы
Примечание: Это не строго, так как это позволяет избежать оценки, если не используется.

Нестрогом = в нестрогой оценке аргументы функции не оцениваются, если они фактически не используются в оценке тела функции.
императив Пример: короткое замыкание
Примечание: _ / _ кажется, это способ проверить, является ли функция нестрогой

таким образом, функция может быть не строгой, но не ленивой. Ленивая функция всегда не является строгой. Call-By-Need частично определяется Позывной, который частично определяется Нестрогом

фрагмент "Ленивые Императивные Языки"

2.1. НЕСТРОГИЙ СЕМАНТИКА VS. Ленивая оценка мы должны сначала уточнить различие между "нестрогой семантикой"и" ленивой оценкой". Non-strictsemantics-это те, которые указывают, что выражение не оценку пока не требуется primitiveoperation. Может быть различные виды нестрогой семантики. Например, нестрогий вызовы процедур donot оценивают аргументы до тех пор, пока их значения требуемый. Конструкторы данных могут иметь non-strictsemantics, в которых составные данные собранный из unevaluated частей ленивая оценка, также вызванная задержанная оценка, метод нормально используемый к реализации не strictsemantics. В разделе 4, эти два метода часто используемые для реализации ленивой оценки очень brieflysummarized.

вызов по значению, вызов ленивым, и вызов по имени "вызов по значению" является общее имя, используемое для вызовов процедур со строгой семантикой. В вызовах по valuelanguages каждый аргумент вызова процедуры оценивается до процедура вызова; стоимость isthen передается процедура или заключительное выражение. Другое имя для вызова по значению "охочих" оценки.Вызов по значению также известен как " applicative order" оценка, потому что все аргументы оцениваются перед функцией применяемый к ним."Call by lazy "(используя терминологию Уильяма Клингера в [8]) - это имя, данное вызовам процедур, которые usenon-strict семантика. В языках с вызовом ленивыми вызовами процедуры, аргументов нет evaluatedbefore подставляется в процедуру тело. Вызов по ленивой оценке также известен как " нормальный порядок " оценка, из-за порядка (от внешнего к внутреннему, слева справа) оценки выражения."Call by name" - это конкретная реализация call by lazy, используемая в Алголе-60 [18]. Этот дизайнеры ofAlgol-60 предназначена для вызова по имени параметры физически заменить в теле процедуры, enclosedby скобках и с соответствующими изменениями имени, чтобы избежать конфликты, прежде чем тело было оцененный.

ВЫЗОВ ЛЕНИВЫМ VS. Вызов по необходимости вызов по необходимости продление звонка ленивым, вызванное наблюдением, что ленивый оценка может beoptimized, вспомнив значение данного отложено выражение, один раз заставила, так что значения не пересчитано, если это необходимо снова. Вызов по оценке потребностей, поэтому расширяет вызов ленивыми методами, используя memoization, чтобы избежать необходимость повторной оценки. Фридман и Мудрые были среди earliestadvocates звонка по параметрам оценки, предполагая, что "самоубийца суспензии", которые самоуничтожились, когда они были впервые оценены, заменяя себя своими ценностями.


Если мы говорим на общем жаргоне информатики, то "ленивый" и "нестрогий", как правило, синонимы-они означают одну и ту же общую идею, которая выражается по-разному для разных ситуаций.

однако в данном конкретном специализированном контексте они могут приобретать различные технические значения. Я не думаю, что вы можете сказать что-нибудь точное и универсальное о том, какая разница между" ленивым "и" нестрогим", скорее всего, будет в ситуации, когда есть разница.