为什么以下Haskell代码是非确定性的?

时间:2015-04-27 03:47:51

标签: haskell

我一直在Learn You A Haskelljust came across the following statement学习Haskell:

  

执行(+) <$> [1,2] <*> [4,5,6]会导致不确定   计算x + y其中x采用[1,2]y的每个值   来自[4,5,6]的每个值。

我不认为我明白什么是非确定性的。只是结果的顺序或计算顺序不能保证每次都相同吗?

3 个答案:

答案 0 :(得分:34)

这本书使用了不同的“非确定性计算”和#34;比你好。

你正在思考&#34;非确定性计算&#34;如同在&#34;一个没有完全确定其输出的程序&#34;。当使用多个并行执行线程时,这种非确定性是常见的;有许多可能的输出,你得到的是由运行时事件的确切顺序任意决定的。

您从LYAH引用的段落正在讨论将列表视为&#34;非确定性计算的模型&#34;,在某种意义上是逻辑编程范式(如果您&#39) ;使用Prolog语言进行了大量的编程,你可能对此很熟悉。在这种意义上,非确定性程序有多个(或零!)输出,因为它们是专门编程的,而不是因为它们没有完全指定它们的输出应该是什么。

如果&#34;非确定性代码&#34;只是具有&#34;类型为t&#34;的零个或多个输出的代码,这听起来很像返回t列表的函数。列表Applicative(和Functor和Monad)实例只是说明如何组合这些&#34;非确定性值&#34;相互之间,具有纯粹的功能。例如,Functor实例说如果你可以将一个函数应用于A来获得一个B,那么你也可以将该函数映射到一个非确定性的A&#34;得到一个非确定的B&#34; (通过将未映射的函数应用于&#34;非确定性A&#34;)的每个可能值。

以这种方式看到的{p> (+) <$> [1,2] <*> [4,5,6]是&#34;非确定性加法&#34;的一个例子。您可以将1或2的数字添加到另一个可能是4,5或6的数字;结果可能是5,6,7,6,7或8(有些可能性会重复,因为生成它们的方法不止一种)。

答案 1 :(得分:23)

在这种情况下,不确定性不是Haskell 执行的计算,而是表示的计算。当被视为monad(或applicative functor)时,列表表示非确定性计算:正如Maybe a计算可能已失败的a,或IO a计算的是执行了一些I / O的a[a]a的非确定性计算。因此,在此解释下,列表[1,2]表示不确定地返回12的计算,以及[4,5,6]的类似计算。或者,再次,通过类比:Haskell中的计算Nothing成功,即使该值表示失败; Haskell中的计算[1,2]是确定性的(而且非常无聊),但是这个值编码了一种非确定性的形式。

因此,(+) <$> [1,2] <*> [4,5,6]不确定地计算 x + y 。同样,这不是代码中写的内容 - 代码代表的内容。代码本身确定性地计算非确定性计算的表示!

这种方法的工作原理是<$><*>函数可以在应用程序仿函数中提升计算,因此代码片段会在列表应用仿函数中计算(+),这意味着它会计算{ {1}}不确定:

  • (+)表示可以返回[1,2]1的计算。调用其结果2
  • x表示可以返回[4,5,6]45中的任何内容的计算。调用其结果6
  • 因此,将这些计算的结果一起添加 - 计算y - 可以评估x + yx的任何可能值的总和。

这就是引用所说的,只是用稍微不同的词语: - )

事实上,y完全等同于(+) <$> [1,2] <*> [4,5,6],其中&#34; nondeterminism&#34;而是[x + y | x <- [1,2], y <- [4,5,6]]x每个迭代各自的列表。这就是非确定性的意思,最终!

至于你是如何考虑理解这一点的:记住,Haskell代码保证在其结果中具有确定性,这要归功于Haskell的纯粹功能性。然而,计算的顺序并不会对此产生影响,因此只要函数不会过早失败(例如,y必须求值为const () undefined),它就会保持相当不受约束的状态。 。我们只通过将其视为一种效果来获得非确定性;列表是这个的一种编码(而()可以是另一种,对于一种非常不同的非确定性)。

答案 2 :(得分:15)

在monad列表中,我喜欢将[1, 2]视为代表一组可能的选择:1或2.当我们对这样的集合进行操作时,我们会产生一组可能的结果。什么是“1或2”加4?当然,“5或6”。在Haskell中,我们可以将该问题标记为(+ 4) <$> [1, 2]并获得预期答案[5, 6]

列表monad代表不确定性,因为它让我们可以讨论可能选择的整个树,而不实际承诺任何这些选择。那么什么是“1或2”加上“4,5或6”?好吧,那可能是:

  • 1
    • + 4 = 5
    • 或+ 5 = 6
    • 或+ 6 = 7
  • 或2
    • + 4 = 6
    • 或+ 5 = 7
    • 或+ 6 = 8

我们可以使用列表monad对Haskell中的问题进行编码(按顺序详尽地计算所有解决方案):

do
  x <- [1, 2]     -- if x is 1 or 2
  y <- [4, 5, 6]  -- and y is 4, 5, or 6
  return (x + y)  -- then what are the possible values of x + y?

或者列表适用(做同样的):

(+) <$> [1, 2] <*> [4, 5, 6]

答案当然是[5, 6, 7, 6, 7, 8]

如果有帮助,您还可以将列表monad或列表推导视为执行某种笛卡尔积。

一种不同的编码方式是立即为每个选项启动一个独立的并发计算,产生最终结果,而不需要任何固有的顺序。