Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么惰性评估有用?_Haskell_Functional Programming_Lazy Evaluation - Fatal编程技术网

Haskell 为什么惰性评估有用?

Haskell 为什么惰性评估有用?,haskell,functional-programming,lazy-evaluation,Haskell,Functional Programming,Lazy Evaluation,我一直在想为什么懒惰的评估是有用的。我还没有让任何人以有意义的方式向我解释;大多数情况下,它最终会变成“相信我” 注:我不是指备忘录 考虑一下: if (conditionOne && conditionTwo) { doSomething(); } 仅当conditionOne为true且conditionTwo为true时,才会执行doSomething()方法。 如果conditionOne为false,为什么需要计算conditionTwo的结果?在这种情况下,条件

我一直在想为什么懒惰的评估是有用的。我还没有让任何人以有意义的方式向我解释;大多数情况下,它最终会变成“相信我”

注:我不是指备忘录

考虑一下:

if (conditionOne && conditionTwo) {
  doSomething();
}
仅当conditionOne为true且conditionTwo为true时,才会执行doSomething()方法。 如果conditionOne为false,为什么需要计算conditionTwo的结果?在这种情况下,条件二的评估将是浪费时间,特别是如果您的条件是某个方法过程的结果


这是惰性评估兴趣的一个例子…

主要是因为它可以更有效——如果不使用值,则不需要计算它们。例如,我可以将三个值传递到一个函数中,但根据条件表达式的顺序,实际上只能使用一个子集。在像C这样的语言中,所有三个值都会被计算出来;但在Haskell中,只计算必要的值


它还允许一些很酷的东西,比如无限列表。在像C这样的语言中,我不能有一个无限列表,但在Haskell中,这没有问题。无限列表在数学的某些领域中使用得相当频繁,因此能够对其进行操作是非常有用的。

当您打开计算机,Windows禁止在Windows资源管理器中打开硬盘上的每个目录,也禁止启动计算机上安装的每个程序时,除非您指出需要某个目录或某个程序,否则这就是“惰性”评估

“惰性”评估是在需要时执行操作。当它是编程语言或库的一个特性时,它是有用的,因为单独实现惰性计算通常比预先计算所有内容都要困难。

如果“惰性计算”是指combound booleans中的like,如

   if (ConditionA && ConditionB) ... 
那么答案很简单,程序消耗的CPU周期越少,运行速度就越快。。。如果一块处理指令对程序的结果没有影响,那么执行它们是不必要的(因此是浪费时间的)

如果是otoh,您指的是我所知道的“懒惰的初始值设定者”,如:


好的,这种技术允许使用类的客户端代码避免为主管数据记录调用数据库,除非使用Employee对象的客户端需要访问主管的数据。。。这使得实例化员工的过程更快,但当您需要主管时,对主管属性的第一次调用将触发数据库调用,数据将被提取并可用

此代码段显示了惰性计算和非惰性计算之间的区别。当然,这个斐波那契函数本身可以进行优化,并使用惰性求值而不是递归,但这会破坏示例

让我们假设我们可能必须使用前20个数字来表示某件事,而不是使用惰性计算,所有的20个数字都必须预先生成,但是,使用惰性计算,它们将仅在需要时生成。因此,您只需在需要时支付计算价格

样本输出

Not lazy generation: 0.023373 Lazy generation: 0.000009 Not lazy output: 0.000921 Lazy output: 0.024205 不懒惰的一代:0.023373 懒惰一代:0.000009 非惰性输出:0.000921 延迟输出:0.024205
导入时间
def now():返回时间
def fibonacci(n):#fibonacci的递归(非惰性)
如果n<2:
返回n
其他:
返回斐波那契(n-1)+斐波那契(n-2)
before1=现在()
notlazy=[fibonacci(x)表示范围(20)内的x]
after1=现在()
before2=现在()
lazy=(范围(20)内x的斐波那契(x))
after2=现在()
before3=现在()
因为我不懒惰:
打印i
after3=现在()
before4=现在()
因为我懒惰:
打印i
after4=现在()
打印“非延迟生成:%f”%(在1之后-在1之前)
打印“延迟生成:%f”%(2之后-2之前)
打印“非惰性输出:%f”%(3之后-3之前)
打印“延迟输出:%f”%(4之后-4之前)

我发现惰性评估在很多方面都很有用

首先,所有现有的惰性语言都是纯的,因为很难对惰性语言的副作用进行推理

纯语言允许您使用等式推理对函数定义进行推理

foo x = x + 3
不幸的是,在非惰性设置中,比惰性设置中有更多的语句无法返回,因此这在像ML这样的语言中不太有用。但在惰性语言中,您可以安全地对等式进行推理

其次,像Haskell这样的惰性语言不需要ML中的“值限制”之类的东西。这将导致语法的大混乱。类似ML的语言需要使用var或fun等关键字。在哈斯凯尔,这些东西归结为一个概念

第三,懒惰可以让您编写非常实用的代码,这些代码可以被理解为片段。在Haskell中,通常编写函数体,如:

foo x y = if condition1
          then some (complicated set of combinators) (involving bigscaryexpression)
          else if condition2
          then bigscaryexpression
          else Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...
这使您能够通过理解函数体“自上而下”地工作。类似ML的语言迫使您使用严格评估的
let
。因此,您不敢将let子句“提升”到函数的主体,因为如果它很昂贵(或有副作用),您不希望总是对它进行评估。Haskell可以明确地将细节“推送”到where子句,因为它知道该子句的内容只会根据需要进行评估

在实践中,我们倾向于使用防护装置并进一步折叠:

foo x y 
  | condition1 = some (complicated set of combinators) (involving bigscaryexpression)
  | condition2 = bigscaryexpression
  | otherwise  = Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...
第四,懒惰有时会为某些算法提供更优雅的表达方式。Haskell中的惰性“快速排序”是一个单行程序,它的好处是,如果只查看前几个项目,您只需支付与选择这些项目的成本成比例的成本。没有什么可以阻止您严格执行此操作,但是您可能每次都必须重新编码算法以获得相同的渐近性能

第五,懒惰允许你定义自己
foo x y = if condition1
          then some (complicated set of combinators) (involving bigscaryexpression)
          else if condition2
          then bigscaryexpression
          else Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...
foo x y 
  | condition1 = some (complicated set of combinators) (involving bigscaryexpression)
  | condition2 = bigscaryexpression
  | otherwise  = Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...
if' True x y = x
if' False x y = y
bool Function(void) {
  if (!SubFunction1())
    return false;
  if (!SubFunction2())
    return false;
  if (!SubFunction3())
    return false;

(etc)

  return true;
}
bool Function(void) {
  if (!SubFunction1() || !SubFunction2() || !SubFunction3() || (etc) )
    return false;
  return true;
}
quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs) ++ [x] ++ quickSort (filter (>= x) xs)
minimum ls = head (quickSort ls)
square x = x * x
square (square (square 2))
> square (square (2 * 2))
> square (square 4)
> square (4 * 4)
> square 16
> 16 * 16
> 256
> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * (square (square 2))
> ((2 * 2) * (square 2)) * (square (square 2))
> (4 * (square 2)) * (square (square 2))
> (4 * (2 * 2)) * (square (square 2))
> (4 * 4) * (square (square 2))
> 16 * (square (square 2))
> ...
> 256
> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * ((square 2) * (square 2))
> ((2 * 2) * (2 * 2)) * ((2 * 2) * (2 * 2))
> (4 * 4) * (4 * 4)
> 16 * 16
> 256
square (square (square 2))

           ||
           \/

           *
          / \
          \ /
    square (square 2)

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
        square 2

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
           *
          / \
          \ /
           2
  if( obj != null  &&  obj.Value == correctValue )
  {
    // do smth
  }
type 'a stack =
    | EmptyStack
    | StackNode of 'a * 'a stack

let rec append x y =
    match x with
    | EmptyStack -> y
    | StackNode(hd, tl) -> StackNode(hd, append tl y)
type 'a lazyStack =
    | StackNode of Lazy<'a * 'a lazyStack>
    | EmptyStack

let rec append x y =
    match x with
    | StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
    | Empty -> y
fix f = f (fix f)
f (f (f ....
fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)
def fix[A](f: A => A): A = f(fix(f))

val fact = fix[Int=>Int] { f => n =>
    if (n == 0) 1
    else n*f(n-1)
}
def fix[A](f: (=>A) => A): A = f(fix(f))

def fact1(f: =>Int=>Int) = (n: Int) =>
    if (n == 0) 1
    else n*f(n-1)

val fact = fix(fact1)
largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0