Section: 3.3 Functions are Non-strict
第 3.3 节 函数是非严格的
Observing lazy evaluation can present difficulties. The essential question is does an expression get evaluated?. While in theory using a non-terminating computation is the way evaluation issues are examined, we need a more practical approach. In expressions, Haskell uses error to denote bottom. Evaluation of error will halt execution and print the attached error message. The error function can be used to create stub functions for program components which have not been written yet or as a value to insert into data structures where a data value is required but should never be used.
观察惰性求值可能会有困难。本质问题是"一个表达式到底会不会被求值?"。
虽然从理论上说,使用一个永不停止的计算是考察求值问题的方法,但我们
需要更实用的办法。在表达式中,Haskell 用 error 表示 bottom(⊥,
即无定义的值)。求值 error 会停止执行并打印附带的错误信息。error
函数可以用来为尚未编写的程序组件创建占位函数,或者作为插入数据结构
中的值——那里需要一个数据值,但它永远不应被使用。
e1 :: Bool -- This can be any type at all!
e1 = error "e1" -- evaluate this and see what happens.
const1 :: a -> Int
const1 x = 1
e2 :: Int
e2 = const1 (error "e2") -- The bottom (the error function) is not
-- needed and will thus not be evaluated.
Section: 3.4 "Infinite" Data Structures
第 3.4 节 "无限"数据结构
Data structures are constructed lazily. A constructor like : will not evaluate its arguments until they are demanded. All demands arise from the need to print the result of the computation -- components not needed to compute the printed result will not be evaluated.
数据结构是惰性构造的。像 : 这样的构造器在它的参数被需要之前不会
对它们求值。所有的"需要"都来自打印计算结果的需求——计算打印结果
用不到的分量不会被求值。
list1 :: [Int]
list1 = (1:error "list1")
e3 = head list1 -- does not evaluate the error
e4 = tail list1 -- does evaluate error
Some infinite data structures. Don't print these! If you do, you will need to interrupt the system or kill the Haskell process.
一些无限数据结构。别打印它们!如果打印了,你就需要中断系统或杀掉
Haskell 进程。
ones :: [Int]
ones = 1 : ones
numsFrom :: Int -> [Int]
numsFrom n = n : numsFrom (n+1)
An alternate numsFrom using series notation:
用序列记法写出的另一个 numsFrom:
numsFrom' :: Int -> [Int]
numsFrom' n = [n..]
squares :: [Int]
squares = map (^2) (numsFrom 0)
Before we start printing anything, we need a function to truncate these infinite lists down to a more manageable size. The take function extracts the first k elements of a list:
在开始打印任何东西之前,我们需要一个函数,把这些无限列表截断到更
易于管理的长度。take 函数提取列表的前 k 个元素:
take :: Int -> [a] -> [a]
take 0 x = [] -- two base cases: k = 0
take k [] = [] -- or the list is empty
take k (x:xs) = x : take (k-1) xs
now some printable lists:
现在是一些可以打印的列表:
e5 :: [Int]
e5 = take 5 ones
e6 :: [Int]
e6 = take 5 (numsFrom 10)
e7 :: [Int]
e7 = take 5 (numsFrom' 0)
e8 :: [Int]
e8 = take 5 squares
zip is a function which turns two lists into a list of 2 tuples. If the lists are of differing sizes, the result is as long as the shortest list.
zip 是一个把两个列表变成一个 2 元组列表的函数。如果两个列表长度不同,
结果的长度与最短的那个列表相同。
zip (x:xs) (y:ys) = (x,y) : zip xs ys
zip xs ys = [] -- one of the lists is []
e9 :: [(Int,Int)]
e9 = zip [1,2,3] [4,5,6]
e10 :: [(Int,Int)]
e10 = zip [1,2,3] ones
fib :: [Int]
fib = 1 : 1 : [x+y | (x,y) <- zip fib (tail fib)]
e11 = take 10 fib
This can be done without the list comprehension:
不用列表推导也可以做到:
fib' :: [Int]
fib' = 1 : 1 : map (\(x,y) -> x+y) (zip fib (tail fib))
This could be written even more cleanly using a map function which maps a binary function over two lists at once. This is in the Prelude and is called zipWith.
如果用一次对两个列表施加二元函数的 map 函数来写,还能更简洁。它就在
Prelude 中,叫 zipWith。
fib'' :: [Int]
fib'' = 1 : 1 : zipWith (+) fib (tail fib)
For more examples using infinite structures look in the demo files that come with Yale Haskell. Both the pascal program and the primes program use infinite lists.
想看看更多使用无限结构的例子,请参考 Yale Haskell 自带的演示文件。
pascal 程序和 primes 程序都用到了无限列表。
Section: 3.5 The Error Function
第 3.5 节 error 函数
Too late - we already used it!
太晚了——我们早就用它了!