代码之家  ›  专栏  ›  技术社区  ›  MikeRand

我会因为懒惰的评价而养成好/坏习惯吗?

  •  9
  • MikeRand  · 技术社区  · 16 年前

    我想用Haskell或F#学习函数式编程。

    有没有什么编程习惯(好的或坏的)会因为Haskell的懒惰评估而形成?为了理解函数式编程,我喜欢Haskell的函数式编程思想。我只是有点担心两件事:

    1. 我可能会将基于懒惰评估的特性误解为“功能范式”的一部分。
    7 回复  |  直到 7 年前
        1
  •  21
  •   Simon Marlow    16 年前

    当你用一种懒惰的语言编程而不是用一种严格的语言工作时,你会养成一些习惯。其中一些对Haskell程序员来说似乎很自然,以至于他们不认为它们是懒惰的评估。我脑子里有几个例子:

    f x y = if x > y then .. a .. b .. else c
      where
        a = expensive
        b = expensive 
        c = expensive
    

    在这里,我们定义了一组子表达式 where

    另一个让人想到的例子是“为事物编号”:

    pairs = zip xs [1..]
    

    在这里,我们只想将列表中的每个元素与其索引相关联,并使用无限列表压缩 [1..] 是Haskell最自然的方式。如果没有无限的列表,你怎么写这个?嗯,折页不太可读

    pairs = foldr (\x xs -> \n -> (x,n) : xs (n+1)) (const []) xs 1
    

    或者可以用显式递归(太冗长,不能融合)编写。还有其他几种书写方法,没有一种方法像 zip

        2
  •  11
  •   Don Stewart    16 年前

    你一定会学到评估策略。对于特定类型的编程问题,非严格的评估策略可能非常有效,一旦您接触到它们,您可能会因为无法在某些语言环境中使用它们而感到沮丧。

    正确的。你将成为一个更全面的程序员。提供“延迟”机制的抽象现在相当普遍,因此如果不了解它们,您将是一个更糟糕的程序员。

        3
  •  6
  •   Beni Cherniavsky-Paskin    16 年前
    1. 我可能会将基于懒惰评估的特性误解为“功能范式”的一部分。

    懒惰的评价 功能范式的重要组成部分。这不是一个要求——你可以用热切的求值进行功能性编程——但它是一个自然适合函数式编程的工具。

        4
  •  1
  •   sclv    16 年前

    扩展Beni的答案:如果我们忽略效率方面的操作方面(暂时坚持纯函数世界),那么在急切求值下的每个终止表达式也在非严格求值下终止,并且两者的值(它们的表示)是一致的。

    下面是一个原因示例:

    现在当你真的想担心效率的时候,差别就很重要了,有时你想严格,有时你不想。

    编辑:受西蒙文章的启发,还有一点:许多问题最自然地被认为是对无限结构的遍历,而不是基本上的递归或迭代。(尽管这样的遍历本身通常会涉及某种递归调用。)即使对于有限的结构,通常您也只想探索潜在的大型树的一小部分。一般来说,非严格的评估允许您停止将处理器实际费心去弄清楚的操作问题与表示您正在使用的实际结构的最自然方式的语义问题混为一谈。

        5
  •  0
  •   Joshua    16 年前

    我希望有坏习惯。

    我看到我的一个同事试图在我们的.NET项目中使用(手工编码的)惰性评估。不幸的是,延迟评估的结果隐藏了一个bug,它将在main开始执行之前尝试远程调用,因此在try/catch之外处理“Hey I cannot connect to the internet”的情况。

    基本上,某种方式隐藏了这样一个事实,即一些非常昂贵的东西隐藏在属性read后面,因此在类型初始值设定项中这样做看起来是个好主意。

        6
  •  0
  •   LennyStackOverflow    16 年前

    我认为这不利于理解、重用和测试。我的计划是将价值生成和价值处理分开。在Haskell中,我会在一个纯函数中生成一个(惰性的)计算值列表,并在另一个(副作用)函数中进行后处理。

    多亏了我的懒惰(双关语)心态,Python代码变得更好了。

        7
  •  -2
  •   KeithS    16 年前

    以简单的C#lambda为例:

    (a,b) => a==0 || ++b < 20
    

    在惰性求值语言中,如果a==0,则表达式++b<20不会求值(因为整个表达式的求值结果都是true),这意味着b不会递增。在命令式语言和函数式语言中,此行为(以及and运算符的类似行为)可用于“隐藏”包含不应执行的副作用的逻辑:

    (a,b) => a==0 && save(b)
    

    在这种情况下,“a”可能是验证错误的数量。如果存在验证错误,则前半部分将失败,而后半部分将不进行评估。如果没有验证错误,则对后半部分进行评估(包括尝试保存b的副作用),并返回结果(显然是真或假)进行评估。如果任何一方的计算结果为false,lambda将返回false,表示b未成功保存。如果这是“急切地”评估,我们会尝试保存,而不管“a”的值是多少,如果非零的“a”表示我们不应该保存,这可能是不好的。

    函数式语言中的副作用通常被认为是不可接受的。然而,很少有不需要至少一个副作用的非平凡程序;通常没有其他方法使函数式算法与非函数式代码或数据存储、显示器、网络通道等外围设备集成。

    推荐文章