如何阅读此功能代码

时间:2018-05-17 01:21:03

标签: functional-programming miranda

无法阅读(解释)此功能性Miranda代码。

g = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) [])

我知道它的作用

  • 通过#
  • 获取长度来计算列表的大小
  • 创建包含原始输入列表的上述长度的单个元素列表
  • 使用foldr将新列表折叠为一个整数,每个元素上的操作+0

但是我对括号感到困惑,并且没有看到输入列表的输入位置。最右边的[]构造函数做了什么?

为什么这段代码只能通过函数g工作,但是如果我直接调用它会引发错误?

2 个答案:

答案 0 :(得分:3)

简而言之,g是一个返回列表长度的函数。

让我们将功能分解为某些部分。

|| returns 1 for any input.
||   return_one "hoobar" = 1
return_one :: * -> num
return_one = (#) . (:[]) 

|| ignore first argument, insert 1 to the second argument.
||   insert_one "hoobar" [4,5,6] = [1,4,5,6]
insert_one :: *->[num]->[num]
insert_one = (:) . return_one

|| sum of list.
||   sum_list [7,8,9] = 24
sum_list :: [num] -> num 
sum_list = foldr (+) 0

|| generate list of 1 which as the same length of original list. 
||   change_one ["apple","banana"] = [1,1]
change_one :: [*] -> [num]
change_one = foldr insert_one []

|| return the length of the list.
||   g ["apple","banana"] = 2
g :: [*] -> num
g = sum_list . change_one

我会解释一些令人困惑的功能。

return_one

(:[])是一个创建单个元素列表的函数,(#)返回长度。 严格来说,(:[])(:),其中[]为第一个参数。

因此(:[]) "hoobar" = "hoobar":[] = ["hoobar"],将(#)应用于它会返回1.

change_one

从空列表[]开始,遍历列表,并在前面插入1。

foldr insert_one [] ["apple","banana"]
= foldr insert_one [1] ["apple"]
= foldr insert_one [1,1] []

答案 1 :(得分:2)

我不太了解米兰达,但基于Haskell(我相信这两者之间的差异会很小,只有#是一个一元的运算符,列表长度是唯一的半 - 显着的并且||是注释语法):.是函数组合:

(p . q) x = p (q x)
  || also can be written as:
p . q = \x -> p (q x)

功能组合是一种关联操作,因此p . (q . r) = (p . q) . r = p . q . r

使用此信息,我们可以使用.

的定义对其进行扩展
g      = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) [])       || Original definition
g list = foldr (+) 0 (foldr ((:) . ((#) . (:[]))) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) (((#) . (:[])) x)) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#) ((:[]) y)) x)) [] list)

这可以清理一下:

g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#)(y:[])) x)) [] list) || More conventional operator syntax for the innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) ((#)(x:[]))) [] list)           || Innermost lambda was applied to x. Substitute y for x.
g list = foldr (+) 0 (foldr (\x -> (:) ((#)([x]))) [] list)            || Apply innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) #[x])) [] list)                 || Remove unnecessary parentheses
g list = foldr (+) 0 (foldr (\x acc -> (:) (#[x]) acc) [] list)        || Explicitly write implicit argument. This particular step is called eta-expansion
g list = foldr (+) 0 (foldr (\x acc -> (:) 1 acc) [] list)             || #[x] is always 1, no matter what x is
g list = foldr (+) 0 (foldr (\x acc -> 1 : acc) [] list)               || More conventional syntax for `:`

另请注意,foldr并未对每个元素应用+0,正如您在问题中所述。 foldr op z (a:b:c:[])变为op a (op b (op c z)))a:b:c:[]是另一种撰写[a,b,c]的方式。我一直认为这个图有助于理解:

foldr diagram

此外,很可能是您在直接调用时遇到错误的原因是p . q x (p . q) x相同。