A';反对意见';在Haskell中,它与对应的Scheme类似

A';反对意见';在Haskell中,它与对应的Scheme类似,haskell,scheme,Haskell,Scheme,作为练习,我在Haskell中实现了一个“cons”操作,它由任意类型的两个值组成一对。实现所需的数据类型非常简单: data Nil = Nil deriving (Eq) data Pair a b = Cons a b deriving (Eq) car (Cons x _) = x cdr (Cons _ y) = y caar = car . car cdar = cdr . car cadr = car . cdr cddr = cdr . cdr *Main> cddr

作为练习,我在Haskell中实现了一个“cons”操作,它由任意类型的两个值组成一对。实现所需的数据类型非常简单:

data Nil = Nil deriving (Eq)
data Pair a b = Cons a b deriving (Eq)

car (Cons x _) = x
cdr (Cons _ y) = y

caar = car . car
cdar = cdr . car
cadr = car . cdr
cddr = cdr . cdr

*Main> cddr (Cons 55 (Cons (1,2,3,4) "hello, world!"))
"hello, world!"
*Main> 
但受此启发,我想让结果对像Scheme列表一样打印出来——包括臭名昭著的“不正确列表”(1 2 3.4)。我的实现(见下文)适用于Char:

*Main> Cons 'a' (Cons 'b' (Cons 'c' Nil))
('a' 'b' 'c')
*Main> Cons 'a' (Cons 'b' 'c')
('a' 'b' . 'c')
*Main> Cons (Cons 'a' 'b')(Cons 'c' (Cons 'd' Nil))
(('a' . 'b') 'c' 'd')
对于Int(或任何其他数据类型)来说,它工作得不太好。所以我的问题是:我怎样才能使它适用于其他数据类型?i、 例如,我希望它能像这样工作:

*Main> Cons 5 (Cons "hello" (Cons False Nil))
(5 "hello" False)
我目前的全面执行情况如下:

data Nil = Nil deriving (Eq)
data Pair a b = Cons a b deriving (Eq)

car (Cons x _) = x
cdr (Cons _ y) = y

caar = car . car
cdar = cdr . car
cadr = car . cdr
cddr = cdr . cdr

instance Show Nil where show _ = "()"

class ShowPair a where
  showRight::a->String

instance (Show a, ShowPair a, ShowPair b)=>Show (Pair a b) where
  show (Cons car cdr) = "(" ++ (show car) ++ (showRight cdr) ++ ")"

instance (Show a, ShowPair a, ShowPair b)=>ShowPair (Pair a b) where
  showRight (Cons car cdr) = " " ++ (show car) ++ (showRight cdr)

instance ShowPair Char where
  showRight x = " . " ++ show x

instance ShowPair Int where
  showRight x = " . " ++ show x

instance ShowPair Nil where
  showRight _ = ""

这里有一个选择。首先,通过将此行放在文件顶部来启用这些扩展名:

{-# LANGUAGE FlexibleInstances, OverlappingInstances, UndecidableInstances#-}
接下来,删除
Char
Int
ShowPair
实例

现在,为具有
Show
的任何内容添加一个
ShowPair
实例:

instance Show a => ShowPair a where showRight = (" . " ++) . show
现在,这可以确保作为
Show
实例的任何类型
a
也是
ShowPair
的实例,通过将
前置到其正常字符串形式来显示。但是,如果某个类型具有更具体的
ShowPair
实例(例如
Nil
),Haskell将使用该实例


这不是标准Haskell的一部分,因此需要启用三种语言扩展。查看以获取有关为什么需要扩展的更多信息。

本在问题的注释中提到了本机对类型,我将在本回答中使用它。我还将用Haskell单元类型
()
替换您的
Nil

这有点超出你的要求,但我认为值得一提。在Haskell中很难在Scheme中捕获“列表”的概念,除非您“欺骗”并使用类似的扩展。这是因为从“纯”的、未扩展的Haskell的角度来看,很难(如果不是不可能的话)将所有Scheme列表指定为同一类型。这意味着,尽管Scheme允许您编写接受任何列表(正确或不正确)的函数,但在Haskell中您将很难做到这一点(并且有充分的理由;不正确的“列表”可能无论如何都不存在)

例如,您基本上选择使用
(a,b)
作为Scheme-like对的类型。现在假设我们有这些方案列表:

(define zero  '())
(define one   '(1))
(define two   '(1 2))
(define three '(1 2 3))
(define four  '(1 2 3 4))
这里有一个简单的Haskell配对翻译,对应于您的操作方式:

zero :: ()
zero = ()

one :: (Integer, ())
one = (1, ())

two :: (Integer, (Integer, ()))
two = (1, (2, ()))

three :: (Integer, (Integer, (Integer, ())))
three = (1, (2, (3, ())))

four :: (Integer, (Integer, (Integer, (Integer, ()))))
four = (1, (2, (3, (4, ()))))
关键在于,在Scheme中,您可以轻松编写一个覆盖所有列表的函数:

(define (reverse list)
 (foldl cons '() list))

(define (filter pred? list)
  (foldr (lambda (elem rest)
           (if (pred? elem)
               (cons elem rest)
               rest))
         '()
         list))

(define (foldl fn init list)
  (if (null? list)
      init
      (foldl fn (fn (car list) init) (cdr list))))

(define (foldr fn init list)
  (if (null? list)
      init
      (fn (car list)
          (foldr fn init (cdr list)))))

在这个Haskell翻译中,您根本无法轻松地做到这一点,因为不同长度的“列表”具有不同的类型。当你考虑<代码>反转<代码>之间的差异时(这是一个长度为n的列表并产生长度n的列表)和<代码>过滤器< /代码>(它取长度n的列表并产生长度m的列表)。≤ Haskell自带了一个内置操作,可以从任意类型的两个值组成一对:
(a,b)
。如果您想像使用函数一样使用它,可以将其拼写为
(,)a b
()
然后接受您的
Nil
部分<代码>汽车然后拼写为
fst
cdr
snd
@Ben明白了-我知道我可能在这里做一些车轮改造。有没有办法让用(,)生成的配对像方案列表一样打印出来?谢谢你的精彩解释和相关帖子的链接!嗯,是的-我知道在Haskell中为“whatever列表”实现
filter
reverse
会有多尴尬-尽管cadr、cddr等都很简单