Haskell剥离语法糖的功能

时间:2015-06-08 12:58:02

标签: haskell syntactic-sugar

我想知道以下函数在没有任何语法糖的情况下会如何显示:

tails1 :: [a] -> [[a]]
tails1 [] = [[]]
tails1 xs@(x:xs') = xs:tails1 xs'

我最关心@运算符的用法,我已经尝试了以下内容,但这显然不是正确的方法

tails1 ((:) x ((:) xs' [])) = xs:tails1 xs'

3 个答案:

答案 0 :(得分:4)

首先,您需要了解列表数据类型。 以下是列表数据构造函数:

[] :: [a]
(:) :: a -> [a] -> [a]

其中:

  1. []创建一个空列表
  2. (:)获取aa列表,并返回a列表,其中包含新元素appedned
  3. 假设您有一个列表[1,2,3,4]。 这可以写成(:) 1 ((:) 2 ((:) 3 ((:) 4 []))) 在表达式x:xs'中,x暂停1xs'将保留(:) 2 ((:) 3 ((:) 4 []))。 换句话说,:接受一个元素和一个列表,将该元素追加到列表中。

    您的示例的等效表达式为:

    tails1 ((:) x xs') = ((:)x xs'):tails1 xs'
    

    其中x持有列表的第一个元素,xs'持有列表的其余部分。 xs'无法容纳多个元素。在您的示例tails1 ((:) x ((:) xs' [])) = xs:tails1 xs'中,xs'应该包含除第一个元素和[]之外的所有内容。 (在我的考试中它应该是2:3:4,这不是一个有效的列表,因为没有以[]结束。)

答案 1 :(得分:3)

在其他答案中,我们看到了两个提案:

tailsA (x:xs') = (x:xs'):tailsA xs
tailsB xs = xs:tailsB (tail xs)

前者具有正确的指称语义,但错误的操作语义。原始tails只有一次致电(:);因此,人们可能会担心,与tailsA的原始定义相比,运行tails时,一个足够愚蠢的编译器会分配额外的利益单元格。后者也有正确的表示,只有一次调用(:),但插入一个全新的函数tail,它没有出现在原文中;这种转变似乎需要一些程序员的洞察力来执行。下面,我将展示如何将原始定义转换为没有at-patterns的定义,不需要程序员洞察(因此可以由编译器执行),并且不会冒额外分配的风险。

tailsC xs = case xs of
    x:xs' -> xs:tailsC xs'

这里特别感兴趣:原始方程式的右侧逐字显示为案例陈述的右侧。 (tailsAtailsB都不包含原始方程式的右侧作为子项。)此特殊情况转换所建议的at模式的一般转换并不完全正确(因为xs匹配任何值,而xs@(x:xs')只匹配非空列表);将Haskell风格的模式转换为GHC-Core风格的case语句(仅检查单个变量,并且其模式都只包含一个构造函数)的完整处理超出了StackOverflow答案的范围。已经有几篇研究论文讨论如何正确,有效地执行此操作并生成快速代码;另请参阅相关问题Algorithm for type checking ML-like pattern matching?以及Google学术搜索中compiling pattern matching搜索的所有结果。

但基本的想法是name@patternpattern匹配时完全匹配(将所有相同的名称pattern绑定到所有相同的值),但另外绑定name达到全部价值。

答案 2 :(得分:2)

因为tails1 []条目是第一位的,所以我们不需要确保desugared tails1 xs@(x:xs')只匹配非空列表,因为它们保证是非空的。因此,撰写xs@(x:xs')的唯一效果是让我们同时访问xstail xs,因此该行可以重写为:

tails1 xs = xs : tails1 (tail xs)