【发布时间】:2015-08-22 22:58:42
【问题描述】:
我想知道如果没有任何语法糖,下面的函数会是什么样子:
tails1 :: [a] -> [[a]]
tails1 [] = [[]]
tails1 xs@(x:xs') = xs:tails1 xs'
我主要关心@运算符的用法,我已经尝试过下面的方法,但这显然不是正确的方法
tails1 ((:) x ((:) xs' [])) = xs:tails1 xs'
【问题讨论】:
我想知道如果没有任何语法糖,下面的函数会是什么样子:
tails1 :: [a] -> [[a]]
tails1 [] = [[]]
tails1 xs@(x:xs') = xs:tails1 xs'
我主要关心@运算符的用法,我已经尝试过下面的方法,但这显然不是正确的方法
tails1 ((:) x ((:) xs' [])) = xs:tails1 xs'
【问题讨论】:
在其他答案中,我们看到了两个建议:
tailsA (x:xs') = (x:xs'):tailsA xs
tailsB xs = xs:tailsB (tail xs)
前者具有正确的指称语义,但错误的操作语义。原来的tails只有一次调用(:);因此,人们可能会担心,与tails 的原始定义相比,一个足够愚蠢的编译器在运行tailsA 时会分配额外的cons 单元格。后者也有正确的外延,只有一次调用(:),但插入了一个全新的函数tail,原来没有出现;这种转变似乎需要一些程序员的洞察力才能执行。下面,我将展示如何以不需要程序员洞察力(因此可以由编译器执行)并且不会冒额外分配风险的方式将原始定义转换为没有 at 模式的定义。
tailsC xs = case xs of
x:xs' -> xs:tailsC xs'
这里特别有趣:原始等式的右侧逐字显示为 case 语句的右侧。 (tailsA 或 tailsB 都不包含原始方程的右侧作为子项。)这种特殊情况转换建议的 at 模式的一般转换并不完全正确(因为 xs 匹配任何值而xs@(x:xs') 仅匹配非空列表);将 Haskell 风格的模式转换为 GHC 核心风格的案例语句(只检查一个变量,并且其模式都只包含一个构造函数)的完整处理超出了 StackOverflow 答案的范围。有几篇研究论文讨论了如何正确、高效地执行此操作并生成快速代码;另请参阅相关问题 Algorithm for type checking ML-like pattern matching? 以及 Google Scholar 搜索 compiling pattern matching 中的所有结果。
但基本思想是name@pattern 在pattern 匹配时完全匹配(将所有相同的名称pattern 绑定到所有相同的值),但另外将name 绑定到完整值。
【讨论】:
首先您需要了解列表数据类型。 以下是列表数据构造函数:
[] :: [a]
(:) :: a -> [a] -> [a]
地点:
a 和 a 列表并返回添加了新元素的 a 列表假设您有一个列表[1,2,3,4]。
这可以写成(:) 1 ((:) 2 ((:) 3 ((:) 4 [])))
在表达式 x:xs'、x 中,1 和 xs' 将保持 (:) 2 ((:) 3 ((:) 4 []))。
换句话说,: 接受一个元素和一个列表,将这个元素附加到列表中。
您的示例的等效表达式是:
tails1 ((:) x xs') = ((:)x xs'):tails1 xs'
x 保存列表的第一个元素,xs' 保存列表的其余部分。 xs' 不能容纳多个元素。在您的示例tails1 ((:) x ((:) xs' [])) = xs:tails1 xs' 中,xs' 应该包含除第一个元素和[] 之外的所有内容。 (在我的例子中,它应该是 2:3:4,这不是一个有效的列表,因为它没有以 [] 结尾)。
【讨论】:
因为tails1 [] 条目首先出现,所以我们不需要确保去糖的tails1 xs@(x:xs') 只匹配非空列表,因为它们保证是非空的。因此,编写xs@(x:xs') 的唯一效果是让我们可以同时访问xs 和tail xs,因此该行可以重写为:
tails1 xs = xs : tails1 (tail xs)
【讨论】: