Section: 4.3 Case Expressions

第 4.3 节 Case 表达式

The function take using a case statement instead of multiple equations:

函数 take 用 case 表达式代替多条方程来写:

take :: Int -> [a] -> [a]
take m ys = case (m,ys) of
             (0  ,_)    -> []
             (_  ,[])   -> []
             (n+1,x:xs) -> x : take n xs

The function take using if then else. We can also eliminate the n+k pattern just for fun. The original version of take is much easier to read!

用 if then else 写的 take 函数。为了好玩,我们还可以把 n+k 模式消掉。

原版 take 可读性好得多!

take' :: Int -> [a] -> [a]
take' m ys = if m == 0 then [] else
              if null ys then [] else
               if m > 0 then head ys : take (m-1) (tail ys)
                else error "m < 0"

Section: 4.4 Lazy Patterns

第 4.4 节 惰性模式

Before the client-server example, here is a contrived example of lazy patterns. The first version will fail to pattern match whenever the the first argument is []. The second version will always pattern match initially but x will fail if used when the list is [].

在客户端-服务器例子之前,先来看一个刻意构造的惰性模式例子。第一个

版本只要第一个参数是 [],模式匹配就会失败。第二个版本初始的模式匹配

总是成功,但当列表为 [] 时,一旦使用 x 就会失败。

nonlazy :: [Int] -> Bool -> [Int]
nonlazy (x:xs) isNull  = if isNull then [] else [x]
e1 = nonlazy [1,2] False
e2 = nonlazy [] True
e3 = nonlazy [] False

This version will never fail the initial pattern match

这个版本永远不会在初始模式匹配上失败:

lazy :: [Int] -> Bool -> [Int]
lazy ~(x:xs) isNull  = if isNull then [] else [x]
e4 = lazy [1,2] False
e5 = lazy [] True
e6 = lazy [] False

The server - client example is a little hard to demonstrate. We'll avoid the initial version which loops. Here is the version with irrefutable patterns.

服务器-客户端的例子有点难演示。我们跳过会死循环的最初版本。这里是

采用不可反驳模式(irrefutable pattern)的版本。

type Response = Int
type Request = Int
client :: Request -> [Response] -> [Request]
client init ~(resp:resps) = init : client (next resp) resps
server :: [Request] -> [Response]
server (req : reqs) = process req : server reqs

Next maps the response from the previous request onto the next request

next 把上一次请求的响应映射为下一次请求:

next :: Response -> Request
next resp = resp

Process maps a request to a response

process 把请求映射为响应:

process :: Request -> Response
process req = req+1
requests :: [Request]
requests = client 0 responses
responses :: [Response]
responses = server requests
e7 = take 5 responses

The lists of requests and responses are infinite - there is no need to check for [] in this program. These lists correspond to streams in other languages.

请求列表和响应列表都是无限的——这个程序里不需要检查 []。这些列表

对应于其他语言中的流(stream)。

Here is fib again:

再来看一次 fib:

fib :: [Int]
fib@(_:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib]
e8 = take 10 fib

Section: 4.5 Lexical Scoping and Nested Forms

第 4.5 节 词法作用域与嵌套形式

One thing that is important to note is that the order of the definitions in a program, let expression, or where clauses is completely arbitrary. Definitions can be arranged 'top down' or bottom up without changing the program.

需要注意的一点是:在一个程序、let 表达式或 where 子句中,各条定义

的先后顺序完全任意。定义可以按"自顶向下"或"自底向上"排列,程序本身

不变。

e9 = let y = 2 :: Float
         f x = (x+y)/y
     in f 1 + f 2
f :: Int -> Int -> String
f x y | y > z  = "y > x^2"
      | y == z = "y = x^2"
      | y < z  = "y < x^2"
  where
    z = x*x
e10 = f 2 5
e11 = f 2 4

Section: 4.6 Layout

第 4.6 节 布局规则

There's nothing much to demonstrate here. We have been using layout all through the tutorial. The main thing is to be careful line up the first character of each definition. For example, if you change the indentation of the definition of f in e9 you will get a parse error.

这里没什么可演示的。整个教程我们都一直在使用布局规则。主要的一点是

要留意对齐每条定义的首字符。例如,如果你改动 e9 中 f 的定义的缩进,

就会得到解析错误。