List 在haskell中手工实现级联

List 在haskell中手工实现级联,list,haskell,concatenation,List,Haskell,Concatenation,如何在Haskell中手动实现连接运算符? 到目前为止,我已经做到了这一点,但最后我还在与递归作斗争: data MyList a = Empty | Element a (MyList a) deriving Show concatenation:: MyList a -> MyList a -> MyList a concatenation Empty Empty = Empty concatenation a Empty = a concatenation Empty a

如何在Haskell中手动实现连接运算符? 到目前为止,我已经做到了这一点,但最后我还在与递归作斗争:

data MyList a = Empty | Element a (MyList a)
 deriving Show

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty Empty = Empty
concatenation a Empty = a
concatenation Empty a = a
concatenation x y = ???

只需使用标准递归:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty l = l
concatenation (Element e l1) l2 = Element e (concatenation l1 l2)
为什么会停止?具有的第二个规则逐个获取第一个列表的每个元素,并将其嵌套在空列表和第二个列表的串联之前,即第二个列表本身第一个规则

  concatenation Element 1 (... Element n (Empty)) l2
= Element 1 (concatenation Element 2 (... Element n (Empty))) l2 -- second rule
= ...
= Element 1 (Element 2 (... Element n (concatenation Empty l2))) -- second rule
= Element 1 (Element 2 (... Element n (l2))) -- first rule

只需使用标准递归:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty l = l
concatenation (Element e l1) l2 = Element e (concatenation l1 l2)
为什么会停止?具有的第二个规则逐个获取第一个列表的每个元素,并将其嵌套在空列表和第二个列表的串联之前,即第二个列表本身第一个规则

  concatenation Element 1 (... Element n (Empty)) l2
= Element 1 (concatenation Element 2 (... Element n (Empty))) l2 -- second rule
= ...
= Element 1 (Element 2 (... Element n (concatenation Empty l2))) -- second rule
= Element 1 (Element 2 (... Element n (l2))) -- first rule

我们可以枚举构造函数,而不是直接使用变量。因此,我们需要涵盖四种情况:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty Empty = -- ...
concatenation Empty (Element b bs) = -- ...
concatenation (Element a as) Empty = -- ...
concatenation (Element a as) (Element b bs) = -- ...
现在我们可以着手解决这些案件。连接两个空列表将返回一个空列表,因此为空

如果第一个列表是空的,而后者不是空的,那么我们返回第二个列表

concatenation Empty (Element b bs) = Element b bs
如果第一个元素是列表,而后者不是,那么我们可以返回第一个列表:

concatenation (Element a as) Empty = Element a as
最后,如果两个列表都非空,则结果列表从第一个列表的第一个元素开始,然后是第一个列表的尾部和第二个列表的连接:

concatenation (Element a as) (Element b bs) = Element a (concatenation as (Element b bs))
这为我们提供了以下代码:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty Empty = Empty
concatenation Empty (Element b bs) = Element b bs
concatenation (Element a as) Empty = Element a as
concatenation (Element a as) (Element b bs) = Element a (concatenation as (Element b bs))
我们可以使用as模式缩短实施时间:

但这相当冗长,而且它在两个列表上都执行模式匹配,这可能很昂贵:例如,如果第二个列表首先需要昂贵的计算,并且第一个列表足够长甚至无限长,那么我们无论如何都不会对第二个列表感兴趣

因此,我们可以看看是否可以压缩条款的数量。前两行可以从以下位置组合在一起:

concatenation Empty Empty = Empty
concatenation Empty (Element b bs) = Element b bs
致:

因为不管第二个列表的值是多少,我们都会返回它。它还为我们潜在地节省了一次评估,这可以产生更有效的代码

我们能对最后两行做类似的事情吗

concatenation (Element a as) Empty = Element a as
concatenation (Element a as) bl@(Element b bs) = Element a (concatenation as bl)
结果,两者都有一个元素构造函数,并有一个元素作为第一个元素。由于as和Empty的连接最终是连接为Empty=as,因此我们也可以在前一种情况下执行递归调用,并使用:

concatenation (Element a as) bl = Element a (concatenation as bl)
这导致:

concatenation :: MyList a -> MyList a -> MyList a
concatenation Empty bl = bl
concatenation (Element a as) bl = Element a (concatenation as bl)
我们可以省略第二个参数:

concatenation :: MyList a -> MyList a -> MyList a
concatenation Empty = id
concatenation (Element a as) = Element a . concatenation as

请注意,最后一个缩减本身并不更有效:这可能会导致将一个潜在的大列表的整个副本构造到内存中,如果第二个列表为空,则会导致大量计算。另一方面,如果计算第二个列表会导致陷入无限循环或昂贵的计算中,而我们对这一部分不感兴趣,我们可能会节省大量CPU周期。

我们可以通过构造函数枚举变量,而不是直接使用变量。因此,我们需要涵盖四种情况:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty Empty = -- ...
concatenation Empty (Element b bs) = -- ...
concatenation (Element a as) Empty = -- ...
concatenation (Element a as) (Element b bs) = -- ...
现在我们可以着手解决这些案件。连接两个空列表将返回一个空列表,因此为空

如果第一个列表是空的,而后者不是空的,那么我们返回第二个列表

concatenation Empty (Element b bs) = Element b bs
如果第一个元素是列表,而后者不是,那么我们可以返回第一个列表:

concatenation (Element a as) Empty = Element a as
最后,如果两个列表都非空,则结果列表从第一个列表的第一个元素开始,然后是第一个列表的尾部和第二个列表的连接:

concatenation (Element a as) (Element b bs) = Element a (concatenation as (Element b bs))
这为我们提供了以下代码:

concatenation:: MyList a -> MyList a -> MyList a
concatenation Empty Empty = Empty
concatenation Empty (Element b bs) = Element b bs
concatenation (Element a as) Empty = Element a as
concatenation (Element a as) (Element b bs) = Element a (concatenation as (Element b bs))
我们可以使用as模式缩短实施时间:

但这相当冗长,而且它在两个列表上都执行模式匹配,这可能很昂贵:例如,如果第二个列表首先需要昂贵的计算,并且第一个列表足够长甚至无限长,那么我们无论如何都不会对第二个列表感兴趣

因此,我们可以看看是否可以压缩条款的数量。前两行可以从以下位置组合在一起:

concatenation Empty Empty = Empty
concatenation Empty (Element b bs) = Element b bs
致:

因为不管第二个列表的值是多少,我们都会返回它。它还为我们潜在地节省了一次评估,这可以产生更有效的代码

我们能对最后两行做类似的事情吗

concatenation (Element a as) Empty = Element a as
concatenation (Element a as) bl@(Element b bs) = Element a (concatenation as bl)
结果,两者都有一个元素构造函数,并有一个元素作为第一个元素。由于as和Empty的连接最终是连接为Empty=as,因此我们也可以在前一种情况下执行递归调用,并使用:

concatenation (Element a as) bl = Element a (concatenation as bl)
这导致:

concatenation :: MyList a -> MyList a -> MyList a
concatenation Empty bl = bl
concatenation (Element a as) bl = Element a (concatenation as bl)
我们可以省略第二个参数:

concatenation :: MyList a -> MyList a -> MyList a
concatenation Empty = id
concatenation (Element a as) = Element a . concatenation as
请注意,最后一个缩减本身并不更有效:这可能会导致将一个潜在的大列表的整个副本构造到内存中,如果第二个列表为空,则会导致大量计算。另一方面,如果计算第二个列表会导致陷入无限循环或昂贵的计算中,而我们对这一部分不感兴趣,我们可能会节省大量CPU周期。

提示:模式匹配Ele 连接构造函数.concatenation元素x xs y=?第一个基本情况是不必要的;如果a与空匹配,则连接a Empty=a工作正常。提示:模式与元素构造函数匹配。连接元素x xs y=?第一个基本情况是不必要的;如果a匹配为空,则串联a Empty=a工作正常。