【问题标题】:Evaluation of nullary functions in Haskell在 Haskell 中评估零函数
【发布时间】:2013-06-13 03:13:26
【问题描述】:

假设你在haskell中有一个nullary函数,它在代码中被多次使用。它总是只评估一次吗?我已经测试了以下代码:

sayHello :: Int
sayHello = unsafePerformIO $ do
    putStr "Hello"
    return 42

test :: Int -> [Int]
test 0 = []
test n = (sayHello:(test (n-1)))

当我调用 test 10 时,它只写了一次“Hello”,所以它表示函数的结果在第一次评估后存储。我的问题是,它有保证吗?我会在不同的编译器中得到相同的结果吗?

编辑 我使用 unsafePerformIO 的原因是检查 sayHello 是否被多次评估。我不在我的程序中使用它。通常我希望 sayHello 每次评估时都有完全相同的结果。但这是一个耗时的操作,所以我想知道是否可以通过这种方式访问​​它,或者是否应该将它作为参数传递到任何需要确保它不会被多次评估的地方,即:

test _ 0 = []
test s n = (s:(test (n-1)))
...
test sayHello 10

根据答案应该使用这个。

【问题讨论】:

    标签: haskell functional-programming lazy-evaluation


    【解决方案1】:

    不存在空函数。 Haskell 中的函数只有一个参数,并且总是有... -> ... 类型。 sayHello 是一个值——Int——但不是一个函数。请参阅this article 了解更多信息。

    关于保证:不,您并没有真正得到任何保证。 Haskell 报告指出 Haskell 是非严格的——所以你知道事情最终会降低到什么价值——但不是任何特定的评估策略。 GHC 通常使用的评估策略是lazy evaluation,即带有共享的非严格评估,但它并没有对此做出强有力的保证——优化器可以对你的代码进行洗牌,以便对事物进行多次评估。

    还有各种例外——例如,foo :: Num a => a 是多态的,所以它可能不会被共享(它被编译成一个实际的函数)。有时,一个纯值可能同时由多个线程评估(在这种情况下不会发生这种情况,因为unsafePerformIO 明确使用noDuplicate 来避免它)。因此,当您编程时,您通常可以预期 懒惰,但如果您想要任何形式的保证,则必须非常小心。报告本身不会真正为您提供任何关于如何评估您的程序的任何信息。

    unsafePerformIO 当然,给你的保证更少。它被称为“不安全”是有原因的。

    【讨论】:

      【解决方案2】:

      sayHello 这样的顶级无参数函数被称为常量应用形式,并且总是被记忆(至少在 GHC 中 - 参见 http://www.haskell.org/ghc/docs/7.2.1/html/users_guide/profiling.html)。您将不得不诉诸一些技巧,例如传递虚拟参数和关闭优化以在全球范围内共享 CAF。

      编辑:引用上面的链接-

      Haskell 是一种懒惰的语言,某些表达式只能 评估一次。例如,如果我们写: x = nfib 25 然后 x 只会被评估一次(如果有的话),并且 对x 的后续需求将立即看到缓存的结果。 x 的定义称为 CAF(常量应用形式),因为 它没有参数。

      【讨论】:

        【解决方案3】:

        如果您确实希望打印 n 次“Hello”,则需要删除 unsafePermformIO,这样运行时就会知道它无法优化对 putStr 的重复调用。我不清楚要不要返回int的列表,所以我写了两个版本的test,一个返回(),一个返回[Int]。

        sayHello2 :: IO Int
        sayHello2 = do
            putStr "Hello"
            return 42
        
        test2 :: Int -> IO ()
        test2 0 = return ()
        test2  n = do
          sayHello2
          test2 (n-1)
        
        test3 :: Int -> IO [Int]
        test3 0 = return []
        test3 n = do
          r <- sayHello2
          l <- test3 (n-1)
          return $ r:l
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-04-09
          • 1970-01-01
          • 2020-03-23
          • 1970-01-01
          • 1970-01-01
          • 2015-05-17
          • 1970-01-01
          相关资源
          最近更新 更多