【发布时间】:2018-10-21 02:06:52
【问题描述】:
我觉得我对函数式编程的知识有点欠缺,所以当我看到this 第一页上的说明时,我决定上网看看并遵循教程以变得更好
“假设您有一个不可变的数字列表 xs = [1,2,3,4,5,6,7,8] 和 一个函数 doubleMe 将每个元素乘以 2,然后 返回一个新列表。如果我们想将列表乘以 8 命令式语言并做了 doubleMe(doubleMe(doubleMe(xs))),它会 可能通过列表一次并制作副本然后返回 它。然后它会再通过列表两次并返回 结果。”
根据我对函数式编程的了解,这似乎是错误的,让我告诉你原因:
doubleMe = (\x.* 2 x)
所以
doubleMe doubleMe doubleMe xs
beta 会减少到:
(\x.* 2 x) doubleMe doubleMe xs ->beta
(* 2 doubleMe) doubleMe xs
(* 2 (\x.* 2 x)) doubleMe xs ->eta
(\x.* 2 * 2 x) doubleMe xs ->beta
(* 2 * 2 doubleMe) xs
(* 2 * 2 (\x.* 2 x)) xs ->eta
(\x.* 2 * 2 * 2 x) xs ->beta
(* 2 * 2 * 2 xs) ->beta
(* 4 * 2 xs) -> beta
(* 8 xs)
这意味着该函数是 beta 等价于 (\x. * 8 x)
我的印象是 Haskell 编译器在执行之前执行了这种缩减,这意味着不,它不会像本教程建议的那样对列表进行 3 次传递,它只会进行一次。我错了吗?如果是这样,那为什么 Haskell 不这样做呢?肯定会大大提高性能。
【问题讨论】:
-
我认为这种优化被称为“融合”,编译器会尝试这样做,但总的来说它很棘手,并且是一个正在进行的研究领域。 stackoverflow.com/q/38905369/14955
-
请注意,引用的部分说的是命令式语言,所以不是在谈论 Haskell 在那里做了什么。也就是说,LYAH sn-p 很困惑——它所描述的区别与命令式与函数式编程语言无关,而与严格与惰性语言有关。无论如何,您对
doubleMe的定义不是 LYAH 所描述的,因为您的定义具有Num a => a -> a类型,但 LYAH 的假设函数具有Num a => [a] -> [a]类型。 -
@Alexis King 我明白了,所以它确实做了我认为的减少,它只是没有一直到 * 8,它对每个元素做了 2*2*2*x执行 3 次操作而不是 1 次,并且只执行 1 遍。 (我有点傻,应该多注意下一段)。
-
@Thilo 这就是我感兴趣的,谢谢!
-
您将
doubleMe doubleMe doubleMe xs与doubleMe (doubleMe (doubleMe xs))混淆了。
标签: haskell