什么是"你在Haskell中通过声明什么是什么而不是声明你如何得到它来进行计算?#34 ;?

时间:2014-05-23 04:43:25

标签: haskell

最近我正在尝试学习函数式编程语言,并选择了Haskell

现在我在读了解你一个haskell here描述似乎是Haskell的哲学我不确定我是否完全理解它:你在Haskell中通过声明什么是什么来进行计算而不是宣布你如何得到它。

假设我想得到一个列表的总和。

声明你如何获得的方式: 通过添加所有元素来获得总和,所以代码将是这样的(不是haskell,python):

sum = 0
for i in l:
    sum += i
print sum

的方式是什么: 总和是第一个元素和其余元素之和的总和,所以代码将是这样的:

sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs

但我不确定我是否得到它。有人可以帮忙吗?感谢。

3 个答案:

答案 0 :(得分:9)

命令式和功能性是解决问题的两种不同方法。

命令(Python)为您提供了用于获取所需内容的操作。例如,您可以告诉计算机"揉面团。然后把它放入烤箱。打开烤箱。烤10分钟。"。

功能(Haskell,Clojure)为您提供解决方案。你更有可能告诉电脑"我有面粉,鸡蛋和水。我需要面包"。电脑碰巧知道面团,但它不知道面包,所以你告诉它"面包是烤好的面团"。计算机知道烘焙是什么,现在知道如何制作面包。你坐在桌旁10分钟,而电脑为你工作。然后,您可以在烤箱里享用新鲜的美味面包。

您可以看到工程师和数学家的工作方式有类似差异。工程师是必不可少的,看着问题并给工人一个蓝图来解决它。数学家定义问题(解决x)和解决方案(x = -----)并且可以使用任何数量的尝试和真实解决方案来解决较小的问题(2x - 1 = ----- => 2x = ----- + 1)直到他终于找到了理想的解决方案。

大学里的人们主要使用功能语言并不是巧合,不是因为它很难学,而是因为大学之外的数学思想家并不多。在你的引文中,他们试图通过巧妙地使用如何 来定义思维过程中的这种差异。我个人认为,每个人都会通过把它们变成他们已经理解的东西来理解单词,所以我想象我的面包比喻应该为你澄清差异。

编辑:值得注意的是,当你强制命令电脑时,你不知道你最后是否有面包(也许你煮得太久了,它被烧了,或者你没有添加足够的面粉)。这在函数式语言中不是问题,您可以准确地知道每个解决方案为您提供的内容。在函数式语言中不需要反复试验,因为你所做的一切都是正确的(虽然并不总是有用,比如意外地解决 t 而不是 x )。 / p>

答案 1 :(得分:4)

解释中缺少的部分如下。

命令性示例逐步显示如何计算总和。 在任何阶段你都无法说服自己这确实是一个列表元素的总和。例如,最初不知道为什么sum=0;它应该是0吗?你循环通过正确的指数; sum+=i给你的是什么。

sum=0 -- why? it may become clear if you consider what happens in the loop,
      -- but not on its own
for i in l:
  sum += i -- what do we get? it will become clear only after the loop ends
      -- at no step of iteration you have *the sum of the list*
      -- so the step on its own is not meaningful

声明性示例在这方面非常不同。在这种特殊情况下,您首先声明空列表的总和为0.这已经是总和的答案的一部分。然后添加一个关于非空列表的语句 - 非空列表的总和是尾部与添加了头元素的总和。这是总和的声明。您可以归纳地证明它找到了任何列表的解决方案。

请注意此证明部分。在这种情况下,很明显。在更复杂的算法中,它并不明显,因此正确性证明是一个重要部分 - 并且请记住,命令性案例仅作为一个整体才有意义。

另一种计算和的方法,希望陈述性和可原性变得更加清晰:

sum [] = 0 -- the sum of the empty list is 0
sum [x] = x -- the sum of the list with 1 element is that element
sum xs = sum $ p xs where -- the sum of any other list is
                          -- the sum of the list reduced with p
  p (x:y:xs) = x+y : p xs -- p reduces the list by replacing a pair of elements
                          -- with their sum
  p xs = xs -- if there was no pair of elements, leave the list as is

在这里我们可以说服自己:1。p使列表更短,因此总和的计算将终止; 2. p生成一个总和列表,因此通过总结更短的列表,我们得到一个只有一个元素的列表; 3.因为(+)是关联的,重复应用p产生的值与原始列表中所有元素的总和相同; 4.我们可以证明(+)的应用程序数量小于直接实现中的应用程序。

换句话说,添加元素的顺序并不重要,因此我们可以先成对([a,b,c,d,e]a+b)对元素(c+d)求和,这为我们提供了一个较短的列表[a+b,c+d,e],其总和与原始列表的总和相同,现在可以以相同的方式减少:[(a+b)+(c+d),e],然后是[((a+b)+(c+d))+e]。< / p>

答案 2 :(得分:3)

罗伯特哈珀声称in his blog&#34;声明&#34;没有任何意义。我想 他在谈论一个明确的定义,我通常认为这个定义更为狭隘 然后意思,但这篇文章仍然值得一试,并暗示你可能没有 找到你想要的答案。

然而,每个人都在谈论&#34;声明&#34;感觉就像我们经常做的那样 谈论同样的事情。即给一些人两种不同的api /语言/程序 并问他们哪个是最具声明性的,他们通常会选择相同的。

起初令人困惑的部分是你的陈述性总和

sum' [] = 0
sum' (x:xs) = x + sum' xs

也可以看作是如何获得结果的指令。它只是一个不同的。 值得注意的是,前奏中的函数sum实际上并未定义为 因为计算总和的特定方式是低效的。事情显而易见 腥。

所以,&#34;什么,而不是&#34;解释似乎对我不满意。我认为它是 陈述性的&#34;如何&#34;此外还有一些不错的属性。我目前的直觉 关于这些属性是什么类似于:

  • 如果事物不会改变任何状态,那么事情就更具说服力。

  • 如果你可以对它进行肮脏的转换以及它的意义,那么事情就更具说明性 事情仍然完好无损。如果我们知道这一点,那么再给你的声明性总和 +是可交换的,有一些理由认为写它就好 sum' xs + x应该会产生相同的结果。

  • 声明性的东西可以分解成更小的东西,但仍然有一些含义。喜欢 xsum' xs在单独处理时仍具有相同的含义,但尝试执行此操作 与python的sum += x相同也不起作用。

  • 如果事物与时间的流动无关,那么它就更具说明性。例如css 没有描述页面加载时网页的样式。它描述了它的造型 网页随时都可以,即使页面会发生变化。

  • 如果您不必考虑计划流程,那么事情就更具说服力。

其他人可能有不同的直觉,甚至是我不知道的定义, 但无论如何,希望这些都有所帮助。