我一直对学习新语言很感兴趣,这一事实使我始终不渝,并使我(我相信)成为一名更好的程序员。我征服Haskell的尝试来来去去-到目前为止两次-我决定是时候再试一次。第三次是魅力,对不对?

不。我重新阅读了我的旧笔记...并感到失望:-(

上次使我失去信心的问题是一个简单的问题:整数的排列。
即从整数列表到列表列表-排列列表:

[int] -> [[int]]

实际上,这是一个通用问题,因此仍将上面的“int”替换为“a”。

从我的笔记:

我先自己编写代码,成功。欢呼!

我将解决方案发送给我的一个好 friend -Haskell大师,这通常有助于向大师学习。他告诉我,这告诉我,“表达了语言的真正力量,使用通用工具为您编写代码需求”。所有这些,我最近喝了kool-aid,让我们开始吧:
permute :: [a] -> [[a]]
permute = foldr (concatMap.ins) [[]]
   where ins x []     = [[x]]
         ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]


让我们分解一下:
bash$ cat b.hs
ins x []     = [[x]]
ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]

bash$ ghci
Prelude> :load b.hs
[1 of 1] Compiling Main             ( b.hs, interpreted )
Ok, modules loaded: Main.

*Main> ins 1 [2,3]
[[1,2,3],[2,1,3],[2,3,1]]

好,到目前为止,很好。花了我一点时间来理解“ins”的第二行,但是可以:
它将第一个arg放在列表中所有可能的位置。凉。

现在,了解foldr和concatMap。在“现实世界的Haskell”中,DOT进行了解释...
(f . g) x

...只是...的另一种语法
f (g x)

在上师发送的代码中,文件夹使用了DOT,其中“ins”函数用作折叠“collapse”:
*Main> let g=concatMap . ins
*Main> g 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]

好的,因为我想了解大师如何使用DOT,所以我根据DOT定义尝试等效表达式(f。g)x = f(g x)...
*Main> concatMap (ins 1 [[2,3]])

<interactive>:1:11:
     Couldn't match expected type `a -> [b]'
            against inferred type `[[[t]]]'
     In the first argument of `concatMap', namely `(ins 1 [[2, 3]])'
     In the expression: concatMap (ins 1 [[2, 3]])
     In the definition of `it': it = concatMap (ins 1 [[2, 3]])

什么!?!为什么?
好的,我检查了concatMap的签名,发现它需要一个lambda和一个列表,但这就是
只是人类的思想; GHC如何应对?根据上面DOT的定义...
(f.g)x = f(g x),

...我所做的是有效的,明智地替换:
(concatMap . ins) x y = concatMap (ins x y)

抓头...
*Main> concatMap (ins 1) [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]

所以... DOT的解释显然是
太简单了... DOT必须足够聪明才能理解
我们实际上是想让“ins”被诅咒并“吃掉”第一个
自变量-从而成为只想对[t]进行操作的函数
(并在所有可能的位置将它们“散布”为“1”)。

但是在哪里指定的呢?当我们调用以下命令时,GHC如何知道如何做到这一点:
*Main> (concatMap . ins) 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]

“ins”签名是否以某种方式传达了……“吃我的第一个论点”政策?
*Main> :info ins
ins :: t -> [t] -> [[t]]        -- Defined at b.hs:1:0-2

我没什么特别的-“ins”是一个带有“t”的函数,
“t”的列表,然后继续创建包含所有“interspersals”的列表。什么都没有“吃掉你的第一个论点并消除它”。

所以在那里...我感到困惑。我了解了(经过一个小时的查看代码!),发生了什么,但是...万能的上帝...也许GHC试图尝试查看可以“剥离”多少个参数?
  let's try with no argument "curried" into "ins",
  oh gosh, boom,
  let's try with one argument "curried" into "ins",
  yep, works,
  that must be it, proceed)

再次-...

而且由于我总是将自己正在学习的语言与已经知道的语言进行比较,因此“ins”在Python中的外观如何?
a=[2,3]
print [a[:x]+[1]+a[x:] for x in xrange(len(a)+1)]

[[1, 2, 3], [2, 1, 3], [2, 3, 1]]

老实说,现在...哪个更简单?

我的意思是,我知道我是Haskell的新手,但是我感觉像个白痴……看了4行代码一个小时,最后假设编译器...尝试了各种解释,直到找到“点击”?

引用致命武器说:“我为此太老了……”

最佳答案

(f . g) x = f (g x)

这是真的。你从那得出结论
(f . g) x y = f (g x y)

也必须是真实的,但事实并非如此。实际上,以下是正确的:
(f . g) x y = f (g x) y

这是不一样的。

为什么会这样呢?好吧(f . g) x y((f . g) x) y相同,并且由于我们知道(f . g) x = f (g x),我们可以将其简化为(f (g x)) y,这又与f (g x) y相同。

因此(concatMap . ins) 1 [[2,3]]等同于concatMap (ins 1) [[2,3]]。这里没有魔术。

解决此问题的另一种方法是通过类型:
.的类型为(b -> c) -> (a -> b) -> a -> cconcatMap的类型为(x -> [y]) -> [x] -> [y]ins的类型为t -> [t] -> [[t]]。因此,如果我们使用concatMap作为b -> c参数,并且使用ins作为a -> b参数,则a成为tb成为[t] -> [[t]]c成为[[t]] -> [[t]](x = [t]y = [t])

因此,concatMap . ins的类型为t -> [[t]] -> [[t]],这意味着一个函数接受任意值和列表列表(任意值)并返回列表列表(相同类型)。

关于haskell - Haskell函数的应用和currying,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3884121/

10-11 03:08