# Clean语言快速体验

Clean语言快速体验

## 基本函数构造

square :: Int -> Int //函数的类型：从IntInt

square n = n * n //值为参数与自身相乘

fac :: Int -> Int

fac 0 = 1

fac n = n * fac (n-1)

Start :: Int

Start = fac 6

Start表达式的值被显示给用户。对于这个例子，程序会写720到控制台。因为这个表达式的值是显示给用户，著名的"Hello world"程序就可这样写：

Start :: String

Start = "Hello world"

power :: Int Int -> Int

power x 0 = 1

power x n = x * power x (n-1)

(^) infixr 8 :: Int Int -> Int

(^) x 0 = 1

(^) x n = x * x ^ (n-1)

## 守卫（Guards）

maximum :: Int Int -> Int

maximum n m

| n < m = m

| n >= m = n

## 高阶函数

twice f x = f (f x)

Start = twice square 2

Start = twice twice square 2

## 多态

I :: t -> t //类型为从类型t到类型t

I x = x

square :: t -> t | * t

square n = n * n

maximum :: t t -> t | < t

maximum n m

| n < m = m

| n >= m = n

maximum函数的类型表明它可以用于有相同类型t的任意两个元素，只要提供的这种类型可以被比较。

## 列表 (Lists)

product :: [t] -> t | one, * t

product [] = one

product [x:r] = x * product r

fac :: Int -> Int

fac n = product [1..n]

## 列表推导式(List comprehensions，又译为列表内涵）

Start = [n*n \\ n <- [1..10] | n rem 3 <> 0]

:: From :== String

:: To :== String

:: Km :== String

table :: [(From,To,Km)]

table = [ ("Amsterdam", "Nijmegen",122 )

, ("Paris" , "Amsterdam" ,490 )

, ("Paris" , "Rome" ,1140)

, ("Berlin" , "Amsterdam" ,705 )

, ("Amsterdam", "Kobenhaven",764 )

, ("Amsterdam", "Rome" ,1640)

, ("Moscow" , "Amsterdam" ,2523)

]

Start =

[ (to, dist)

\\ ("Amsterdam", to, dist) <- table

| dist < 1000

]

Start =

[ (to, dist)

\\ ("Amsterdam", to, dist) <- table ++ [(t2, t1, d) \\ (t1, t2, d) <- table]

| dist < 1000

]

Start =

[ (to, dist1 + dist2)

\\ ("Amsterdam", t2, dist1) <- connections

, (t3 , to, dist2) <- connections

| t2 == t3

&& to <> "Amsterdam"

]

where connections = table ++ [(t2, t1, d) \\ (t1, t2, d) <- table]

Start =

[ (n, to, dist)

\\ (to, dist) <- fromAmsterdam

& n <- [1..]

]

where fromAmsterdam =

[ (to, dist)

\\ ("Amsterdam", to, dist) <- table ++ [(t2, t1, d) \\ (t1, t2, d) <- table]

]

## 记录

Clean有一个与关系数据库系统和许多现代编程语言类似的记录的概念。一个简单例子是记录类型Product。这个类型的记录包含三个字段：一个String类型的产品名，一个Real类型的价格，一个String类型的供货商:

:: Product = { name :: String

,price :: Real

,supplier :: String

}

beer :: Product

beer = { name = "Grolsch Beer"

,price = 0.80

,supplier = "Groenlo Brewery"

}

Order = { customer :: String

, date :: Date

, contents :: [Item]

}

:: Item = { product :: String

, quantity :: Int

}

myOrder :: Order

myOrder = { customer = "Pieter"

, date = "30 jan '98"

, contents = [ { product = "Grolsch Beer"

, quantity = 5

}

, { product = "Pizza"

, quantity = 1

}

]

}

total :: Order [Product] -> Real

total order products =

sum [ toReal i.quantity * p.price

\\ i <- order.contents

, p <- products

| p.name == i.product

]

SELECT sum (i.quantity * p.price)

FROM myOrder.contents i, Product p

WHERE p.name = i.product

## 类和重载

Clean有一个非常强大的类的概念。这个“类”与你知道的来自面向对象语言中的“类”有很多不同，了解这一点对理解Clean中类的概念很重要。在Clean中，一个类是带有相同名称的一个函数家族。这些族成员的不同是类型的处理。作为一个非常简单的例子考虑下面的增加函数类：

class inc t :: t -> t

instance inc Int where

inc i = i+1

instance inc Real where

inc r = r+1.0

instance inc Item where

inc i = {i & quantity = inc i.quantity}

instance < Product where

(<) p1 p2 = p1.name < p2.name

qsort :: [t] -> [t] | < t

qsort [] = []

qsort [e:r] = qsort [ x \\ x <- r | x

++ [e] ++

qsort [ x \\ x <- r | x>=e ]

class >= a where

(>=) infix 4 :: a a -> Bool | < a

(>=) x y :== not (x

## 隐式类

addVAT :: t -> t | toReal, fromReal t

addVAT p = fromReal ((1.0 + VAT) * toReal p)

VAT :== 0.175

## 代数数据类型

:: Day = Sun | Mon | Tue | Wed | Tur | Fri | Sat

:: List t = Nil | Cons t (List t)

Tree表示任意类型的树。一棵树要么是空的，要么有一个包含Tree a类型的左子树、一个a类型的值和一个右子树的节点。

:: Tree a = Empty | Node (Tree a) a (Tree a)

a_tree :: Tree [Int]

a_tree = Node (Node Empty [] Empty) [1..3] Empty

Trees可以被普通函数操作。通常它方便地用不同的候选函数对不同的构造子进行操作，用模式匹配选择一个构造子的参数。例如翻转一棵树的函数被定义为：

Flip :: (Tree a) -> Tree a

Flip Empty = Empty

Flip (Node left elem right) = Node (Flip right) elem (Flip left)

toList :: (Tree t) -> [t]

toList Empty = []

toList (Node l elem r) = toList l ++ [elem] ++ toList r

insertTree :: a (Tree a) -> Tree a | < a

insertTree x Empty = Node Empty x Empty

insertTree x (Node l y r)

| x < y = Node (insertTree x l) y r

| otherwise = Node l y (insertTree x r)

toTree :: [a] -> Tree a | < a

toTree [] = Empty

toTree [hd:tl] = insertTree hd (toTree tl)

tsort :: ([a] -> [a]) | < a

tsort = toList o toTree

## 最后的话

Clean的静态类型系统保证运行时类型错误不会发生。也就排除了程序对这种错误进行测试的需要。通过编译器验证类型一致性后程序更快、更安全。你可以花更多时间来验证和改进程序行为。

0

0