Почему это утверждение на Haskell не оценивается лениво? - PullRequest
8 голосов
/ 08 июля 2010

У меня определена следующая функция:

ex 1 x = 1
ex 0 x = 0
ex b x = b ** x

Затем, когда я выполню следующее:

1 `ex` (sum [1..])

он пытается вычислить сумму бесконечной последовательности, вместо того, чтобы быть ленивым и возвращать 1. Почему?


РЕДАКТИРОВАТЬ: После дальнейшего изучения я обнаружил, что лень происходит, если я определяю функцию ex в файле, но не если я определяю ее в GHCI:

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x
Prelude> ex 1 (sum [1..])
<interactive>: out of memory (requested 1048576 bytes)

Если я вытяну определение ex в файл (в данном случае test.hs):

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> :load test.hs 
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> ex 1 (sum [1..])
1.0

Тогда возникает новый вопрос: почему?

Ответы [ 3 ]

17 голосов
/ 08 июля 2010

В GHCi каждый оператор let вводит новое определение ex вместо нескольких случаев шаблона, как вы ожидаете. Так что зависает, потому что, когда вы вводите ex 1 (sum [1..]) впоследствии, существует только окончательная версия ex b x = b ** x.

Если вы хотите определить функцию с несколькими случаями шаблонов в GHCi, вам нужно поместить ее в один оператор let, например:

let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x

То же самое относится ко всему, что обычно пишется в несколько строк, например, к записи do. Например, такая функция:

f x = do
    y <- get
    put (x + y)
    return y

Должно быть написано так в GHCi:

let f x = do { y <- get; put (x + y); return y }
1 голос
/ 08 июля 2010
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x

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

Чтобы определить одну функцию с двумя шаблонами, используйте let ex 1 x = 1; ex b x = b ** x, т.е. разделяйте регистры точкой с запятой.

0 голосов
/ 08 июля 2010

Я пропустил одну точку лени, что делает ответ ниже ложным.


Потому что sum вычисляет общую сумму всех элементов в последовательности.Что, в вашем случае, бесконечно.

Вы, вероятно, хотите

map ((curry ex) 1) [1..]

То есть

map -- map each item x to y
    (
        (
            curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z
        )
        1 -- then invoke it with 1, which results in y -> z, x being 1
    )
    [1..] -- the infinite sequence to be mapped.
...