Haskell如何进行后期评估?似乎还为时过早?

问题描述 投票:0回答:2

我听说haskell的评估较晚。但是,无论我尝试做什么,似乎都可以像其他编程语言一样对它进行评估。

请考虑以下代码:

test :: Bool -> IO()

test n = do
  let y = 5
  print n


main = do
  let y = 8
  test (y == 8)

此代码输出:

True
  • 为什么此代码要进行早期评估?
  • 我可以举一个在Haskell进行后期评估的例子吗?

谢谢。

haskell lazy-evaluation
2个回答
0
投票

首先要记住的一些重要的一般信息:

Haskell变量是immutable总是无一例外,对此您无能为力。

因此,当您编写类似let y = 5的内容时,它确实从不更改某些已经存在的变量的值。相反,它引入了一个名为y的新变量,并为其提供了所需的值。程序中是否还有一个变量y无关紧要,这完全是different变量。实际上,请考虑以下问题:

main :: IO ()
main = do
   let y = 1
   do let y = 2
      print y
   print y

输出为

2
1

在您的示例中,let y = 5语句完全无效,很可能会被编译器完全丢弃。实际上,如果您使用-Wall进行编译(应该使用),GHC会告诉您:

/tmp/wtmpf-file30239.hs:4:7: warning: [-Wunused-local-binds]
    Defined but not used: ‘y’
  |
4 |   let y = 5
  |       ^

尤其是,y == 8检查可能不受您可以使用的任何let y =语句的影响。

实际上,更普遍地说,惰性评估不会影响值。那是拥有一种纯粹的功能性语言的伟大之处之一:因为一切都是不变的,所以惰性求值通常不会影响值语义,它只会影响完成相同工作的顺序–这[影响如何快速终止(或终止是否终止),但完成后不会产生什么价值。在运行时进入y == 8函数之前或之后评估test都无关紧要,实际上,Haskell标准并没有说明任何事情–它只说明test是否以甚至不使用参数值,则无终止参数将不会阻止test终止(非严格语义)。因此,以下内容展示了行之有效的惰性评估:

unobtanium :: Bool unobtanium = unobtanium -- infinite loop don'tTest :: Bool -> IO () don'tTest a = do putStrLn "Meh, I'm too lazy to do it." main :: IO () main = do let y = unobtanium don'tTest y
...即使无法评估y

0
投票
为什么此代码要进行早期评估?

不是,正如@leftaroundabout所讨论的。这里的误解是关于变量作用域而不是评估。

我可以举一个在Haskell进行后期评估的例子吗?

无限列表

我发现最早的容易被懒惰评估的例子是无限列表。考虑典型的斐波那契数列。为了命名起见,一个详细的版本是:

fibs :: [Integer] fibs = 0 -- First element : 1 -- Cons with the second element : zipWith (\first second -> first + second) fibs (drop 1 fibs) -- Cons with all other elements, dynamically computed.

有些人不喜欢计算。这些人的一个例子是重复列表。考虑一个永无止境的42链表...这在C语言中很难以一种有用的方式完成。

infiniteFortyTwos = 42 :infiniteFortyTwos

错误

错误可能是最常见的懒惰评估,但是大多数人并没有真正意识到正在发生懒惰评估。当与布尔操作一起使用时,通常称为短路,请考虑shell脚本:

var=$(ls /dir/that/does/not/exist 2>/dev/null || echo DNE)

这个习惯用法在编程和开发中无处不在。 Perl早年经常使用|| die "error"。在Haskell中,这些类型不鼓励以这种特定方式进行评估和失败的混合,但是惰性功能是相同的。

考虑这个构思不佳的例程:

headEquals :: Eq a => a -> [a] -> Bool headEquals v xs = not (null xs) && v == head xs

或常见的Java和C模式:

if(ptr != NULL && *ptr == value) { ... }

打结

更高级的惰性评估技术正在解决这一难题。例如,假设我们要用列表中最小的元素替换列表中的所有元素。让我们假设在计算答案时有答案,然后使用返回值作为答案:

minList :: [Int] -> [Int] minList [] = [] minList (m:xs) = let (theMin, newList) = go theMin m xs in theMin : newList where go realAnswer partialAnswer [] = (partialAnswer, []) go realAnswer partialAnswer (y:ys) = let newPartialAnswer = min y partialAnswer in (realAnswer :) <$> go realAnswer newPartialAnswer ys

© www.soinside.com 2019 - 2024. All rights reserved.