Простой пример вызова по необходимости - PullRequest
0 голосов
/ 19 января 2019

Я пытаюсь понять теорему «по требованию». Я понимаю определение, но я немного сбит с толку. Я хотел бы увидеть простой пример, который показывает, как работает вызов по требованию.

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

Я читал о вызове по имени в Scala и понимаю, что вызов по имени и вызов по необходимости похожи, но отличаются тем, что вызов по потребности сохранит оцененное значение. Но я бы очень хотел увидеть пример из реальной жизни (он не обязательно должен быть в Хаскеле), который показывает вызов по необходимости.

Ответы [ 2 ]

0 голосов
/ 19 января 2019

обновление: простой пример:

ff 0 = 1
ff 1 = 1
ff n = go (ff (n-1))
  where
  go x = x + x

При вызове по имени каждый вызов go оценивает ff (n-1) дважды, каждый для каждого появления x в своем определении (поскольку + является строгим в обоих аргументах, т.е. требует значений оба).

При вызове по требованию аргумент go вычисляется не более одного раза. В частности, здесь значение x определяется только один раз и используется повторно для второго появления x в выражении x + x. Если бы в этом не было необходимости, x не был бы оценен вообще, так же, как и вызов по имени.

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


Вот мое понимание этого в контексте Haskell.

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

Позвоните по имени:

take 10 . filter even $ [1..]

С одним потребителем произведенная стоимость исчезает после производства, так что это также может быть вызов по имени.

Позвоните по необходимости:

import qualified Data.List.Ordered as O

h = 1 : map (2*) h <> map (3*) h <> map (5*) h
    where
    (<>) = O.union

Разница в том, что здесь список h повторно используется несколькими потребителями в разных темпах, поэтому важно, чтобы полученные значения запоминались. В языке вызовов по имени здесь было бы много копий вычислительных усилий, потому что вычислительное выражение для h будет подставляться в каждом из его вхождений, вызывая отдельные вычисления для каждого. В языке по требованию - способном , таком как Haskell, результаты вычисления элементов h распределяются между каждой ссылкой на h.

Другим примером является то, что большинство любых данных, определенных fix, доступны только по запросу. С помощью кол-ва по значению мы можем получить комбинатор Y .

См .: Совместный доступ и неразделение комбинатора с фиксированной точкой и связанных с ним записей и комментариев (среди них this и его ссылки, например Можно ли использовать метод fold для создания бесконечных списков? ).

0 голосов
/ 19 января 2019

Функция

say_hello numbers = putStrLn "Hello!"

игнорирует аргумент numbers. В семантике call-by-value , даже если аргумент игнорируется, может потребоваться оценка параметра на сайте вызова функции, возможно, из-за побочных эффектов, от которых зависит остальная часть программы.

В Хаскеле мы можем назвать say_hello как

say_hello [1..]

где [1..] - это бесконечный список натуральных объектов. В соответствии с семантикой вызова по значению ЦП будет запускаться, пытаясь создать бесконечный список, и вообще никогда не попадет в say_hello!

Haskell просто выводит

$ runghc cbn.hs
Hello!

Для менее драматичных примеров первые десять натуральных чисел

ghci> take 10 [1..]
[1,2,3,4,5,6,7,8,9,10]

Первые десять шансов

ghci> take 10 $ filter odd [1..]
[1,3,5,7,9,11,13,15,17,19]

В семантике «по требованию» каждое значение - даже концептуально бесконечное, как в приведенных выше примерах - оценивается только в требуемой степени и не более.

...