Haskell Cons运算符(:)

Haskell Cons运算符(:),haskell,functional-programming,operators,Haskell,Functional Programming,Operators,我对Haskell真的很陌生(事实上,我昨天从O'Reilly那里看到了“真实世界的Haskell”并想“嗯,我想我会学习函数编程”),我想知道:我可以使用构造操作符在列表的开头添加一个项: 1 : [2,3] [1,2,3] 我尝试创建一个在书中找到的示例数据类型,然后使用它: --in a file data BillingInfo = CreditCard Int String String | CashOnDelivery | Invoice Int deriving (Show)

我对Haskell真的很陌生(事实上,我昨天从O'Reilly那里看到了“真实世界的Haskell”并想“嗯,我想我会学习函数编程”),我想知道:我可以使用构造操作符在列表的开头添加一个项:

1 : [2,3]
[1,2,3]
我尝试创建一个在书中找到的示例数据类型,然后使用它:

--in a file
data BillingInfo = CreditCard Int String String
| CashOnDelivery
| Invoice Int
deriving (Show)

--in ghci
 $ let order_list = [Invoice 2345]
 $ order_list
[Invoice 2345]
 $ let order_list = CashOnDelivery : order_list
 $ order_list
[CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, CashOnDelivery, ...-
等等。。。它只是永远重复,这是因为它使用惰性评估吗

--编辑--

好吧,让order\u list=CashOnDelivery进入我的脑海:order\u list不会将CashOnDelivery添加到原始的order\u列表,然后将结果设置为order\u list,而是递归的,创建一个无限的列表,永远将CashOnDelivery添加到它的开头。当然,现在我记得Haskell是一种函数式语言,我不能更改原始order_列表的值,所以我应该怎么做才能简单地“将它固定到列表的末尾(或开头,无论什么)” 生成一个函数,该函数将列表和BillingInfo作为参数,然后返回列表

--编辑2--


好吧,根据我得到的所有答案以及无法通过引用传递对象和变异变量(如我习惯的)的情况。。。我想我只是过早地问了这个问题,我真的需要深入研究功能范式,然后才能真正理解我问题的答案。。。我想我想要的是如何编写一个函数或其他东西,获取一个列表和一个项目,然后返回一个同名的列表,这样函数就可以被多次调用,而不必每次更改名称(就好像它实际上是一个程序,将实际订单添加到订单列表中,用户不必每次都为该列表想出一个新名称,而是将一个项目附加到同一列表中).

您刚刚创建的cons的cdr指向它自己:第二个
let
中使用的
order\u list
的定义就是正在创建的定义。使用不同的变量名来完全回避递归问题,代码也会比较容易混淆


编辑:在阅读了Joel的答案后,我似乎在用口齿不清的语言说话。不管是惰性求值还是否,在任何情况下,您都创建了一个递归定义…

Haskell使用惰性求值…在需要之前,不会对任何内容求值,这就是为什么order_list存储为包含CashOnDelivery和另一个未求值的单元格引用或再次列出顺序。

我想你的意思是“这是因为它使用惰性评估吗”?答案是肯定的:

let ol = CashOnDelivery : ol

这告诉我们ol包含元素CashOnDelivery,然后是表达式ol的结果。这个表达式在必要时才进行计算(因此:laziness)。因此,当打印ol时,将首先打印CashOnDelivery。只有这样才能确定列表的下一个元素,从而导致无限行为。

是的,您正在尝试打印一个无限列表,可以通过延迟求值创建。例如

let a = 1 : a
创建一个无限的列表,您可以使用take函数获取任意数量的列表,或者在尝试打印时获取。请注意,您在等式的左侧和右侧使用相同的标识符,它可以工作:order_list是CashOnDelivery:order_list,现在替换为:order_list=CashOnDelivery:(CashOnDelivery:order_list)=现金等

如果您想创建[现金…,发票]列表,不要重复使用这样的名称。

X:L的意思是“创建一个以X开头的列表,然后是由L定义的列表中的项目。”


然后,您将订单列表定义为CashOnDelivery,后面是列表定义的订单列表中的项目。此定义是递归的,这就是为什么列表求值会不断返回CashOnDelivery。您的列表实际上包含一个内部数目的CashOnDelivery值,后面是一个发票值。

问题的答案编辑:

那么,对于一个简单的“将其附加到列表的末尾(或开头,无论什么)”,我应该怎么做?创建一个函数,将列表和BillingInfo作为参数,然后返回一个列表

啊,但是已经有一个“函数”用于在列表元素的前面加上前缀:它是cons
(:)
构造函数:-)

因此,只要您对两个不同的变量不使用相同的名称,您现有的代码就可以正常工作,因为第二个名称绑定将阴影(隐藏)第一个

ghci> let first_order_list = [Invoice 2345]
ghci> first_order_list
[Invoice 2345]
ghci> let second_order_list = CashOnDelivery : first_order_list
ghci> second_order_list
[CashOnDelivery, Invoice 2345]

关于第二次编辑:

既然你问在一个实际的项目中你将如何做这样的事情,我想说:

如果你不断地在列表中添加内容,你就不想每次都为该列表创造新的名称。但是这里的关键字是“重复”,在命令式编程中,您将在这里使用循环(并修改循环中的某些变量)

因为这是函数式编程,所以不能使用循环,但可以使用递归。以下是我将如何编写一个程序,允许用户输入订单并收集列表:

main = do
  orderList <- collectBillingInfos
  putStrLn ("You entered these billing infos:\n" ++ show orderList)

collectBillingInfos :: IO [BillingInfo]
collectBillingInfos = loop []
  where
    loop xs = do
      putStrLn "Enter billing info (or quit)"
      line <- getLine
      if line /= "quit"
        then loop (parseBillingInfo line : xs)
        else return xs

parseBillingInfo :: String -> BillingInfo
parseBillingInfo _ = CashOnDelivery -- Don't want to write a parser here
如果没有延迟求值,那么对
take
的调用将崩溃,因为它将首先尝试求值
order\u list
(无限)

现在,对于顺序列表来说,这并不是很有用,但在许多其他地方,能够使用无限(或非常大)的数据结构编程非常方便。

您可以这样做:

$ let order_list = [Invoice 2345]
$ let order_list = CashOnDelivery : order_list
这里需要注意的重要一点是,您不仅仅是在第一个
订单列表中添加
CashOnDelivery
项目。您正在定义一个与第一个变量无关的新变量
order\u list
。这是一个递归定义,右侧的
order\u列表
指的是您在左侧定义的
order\u列表
,而不是前一行中定义的。由于这种递归,您将得到一个无限列表

我怀疑你真的想做这样的事情:
$ let order_list = [Invoice 2345]
$ let order_list = CashOnDelivery : order_list
$ let order_list = [Invoice 2345]
$ order_list
[Invoice 2345]
$ let order_list2 = CashOnDelivery : order_list
$ order_list2
[CashOnDelivery, Invoice 2345]
let order_list = CashOnDelivery : order_list
let order_list = [Invoice 2345]
let order_list = CashOnDelivery : order_list
import Control.Monad.Identity

let order_list = runIdentity $ do
       order_list <- return [Invoice 2345]
       order_list <- return $ CashOnDelivery : order_list
       return order_list
val fin = fn _ => 0
val fin = fn x => fin x + 1
(* the second `fin` is calling the first `fin` *)

val rec inf = fn x => inf x + 1
(* ML must be explicitly told to allow recursion... *)
fun inf' x = inf' x + 1
(* though `fun` is a shortcut to define possibly recursive functions *)
let inf = \_ -> 0
let inf = \x -> inf x + 1
-- the second `inf` completely shadows the first `inf`
foo :: Maybe [Int]
foo = do
    x <- return [1]
    x <- return (0 : x)  -- rhs `x` refers to previous `x`
    return x
-- foo == Just [0, 1]

bar :: Maybe [Int]
bar = mdo
    y <- return (0 : y)  -- rhs `x` refers to lhs `x`
    return y
-- bar == Just [0, 0, 0, ...]