Haskell 中的语法糖
语法糖
下面一段摘自:http://hi.baidu.com/kuangxiangjie/blog/item/f20743fb81bf6b106d22ebd1.html
在Wiki里关于语法糖的一些历史介绍:
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)
创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜“的语法。语法糖往往给程序员提供了更实用的
编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
Wiki里提到,在C语言里用a[n]表示*(a+n),用a[n][m]表示*(*(a+n)+m),也是语法糖。
实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,
但是C++更进一步的推广了这种风格,更好用了。
按照Wiki的理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。
现在我们介绍一下 Haskell 中的语法糖,如有错误,请指正,改之
1.缩进
Haskell 也像 C 语言一样可以用大括号 '{}' 和分号 ';' 来构造程序块,但这样的写法会造成程序难以阅读,为了解决这个问题,Haskell 引入了缩进。
注意 Haskell 的缩进不同于 Python 的缩进。在 Python 中,tab/空格 是语法,而 Haskell 中 tab/空格 是定义良好的语法糖,最终会被转换成
括号和分号的程序块的语法。
使用缩进的结构有:let,where,case of,... ? (其他的就不了解了)。在它们之后的缩进都可以转换为括号和分号的程序块语法。
2.List
List 的最终转换如下:
- [1, 2, 3, 4] => 1:2:3:4:[]
- [1..10] => enumFromTo 1 10
- [1, 3..10] => enumFromThenTo 1 3 10
- [1..] => enumFrom 1
- [1, 3..] => enumFromThen 1 3
复制代码
- Prelude> :t enumFrom
- enumFrom :: (Enum a) => a -> [a]
- Prelude> :t enumFromTo
- enumFromTo :: (Enum a) => a -> a -> [a]
- Prelude> :t enumFromThen
- enumFromThen :: (Enum a) => a -> a -> [a]
- Prelude> :t enumFromThenTo
- enumFromThenTo :: (Enum a) => a -> a -> a -> [a]
- Prelude> enumFromTo 1 10
- [1,2,3,4,5,6,7,8,9,10]
- Prelude> enumFromThenTo 1 3 10
- [1,3,5,7,9]
- Prelude> enumFrom 1
- [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
- ...
- ...
- 15732,15733,15Interrupted.
复制代码
3.String
String 其实也是 List,之所以把它单独列出来,是因为 String 和 List 写法不一样,前者被双引号括住,而后者被中括号括住
String 的最终转换如下:
- "abcde" => 'a':'b':'c':'d':'e':[]
复制代码
4.List Comprehensions
List Comprehensions 等同于 map, filter 函数,但比 map, filter 函数更直观。
List Comprehensions 的最终转换如下:
- [ x | x <- aList , f x]
- =>
- let ok x = if f x
- then [x]
- else []
- in concatMap ok aList
复制代码
- Prelude> :t concatMap
- concatMap :: (a -> [b]) -> [a] -> [b]
复制代码
5.Section
Section 最终会转换为 lambda 函数
例:
- (+2)
- => x -> x+2
- (*2)
- => x -> x*2
复制代码
6. infix/prefix
- 1 + 2 => (+) 1 2
- take 1 [0, 1] => 1 `take` [0, 1]
复制代码
7. Named Fields
- data Configuration =
- Configuration { username :: String,
- localhost :: String,
- remotehost :: String,
- isguest :: Bool,
- issuperuser :: Bool,
- currentdir :: String,
- homedir :: String,
- timeconnected :: Integer
- }
- 会转换为:
- data Configuration =
- Configuration String -- user name
- String -- local host
- String -- remote host
- Bool -- is guest?
- Bool -- is super user?
- String -- current directory
- String -- home directory
- Integer -- time connected
- deriving (Eq, Show)
- username :: Configuration -> String
- userName (Configuration un _ _ _ _ _ _ _) = un
- localhost :: Configuration -> String
- localHost (Configuration _ lh _ _ _ _ _ _) = lh
- remoteHost :: Configuration -> String
- remoteHost (Configuration _ _ rh _ _ _ _ _) = rh
- isGuest :: Configuration -> Bool
- isGuest (Configuration _ _ _ ig _ _ _ _) = ig
- ...
复制代码
- hostData (Configuration {localhost=lh,remotehost=rh}) = (lh,rh)
- 会转换为:
- hostData (Configuration _ lh rh _ _ _ _ _) = (lh,rh)
复制代码
- initCFG' =
- Configuration
- { username="nobody",
- localhost="nowhere",
- remotehost="nowhere",
- isguest=False,
- issuperuser=False,
- currentdir="/",
- homedir="/",
- timeconnected=0 }
- 会转换为:
- initCFG = Configuration "nobody" "nowhere" "nowhere" False False "/" "/" 0
复制代码
8.do
do 也是语法糖
- main = do
- putStrLn "Greetings! What is your name?"
- inpStr <- getLine
- putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
复制代码
=>
- main =
- putStrLn "Greetings! What is your name?" >>
- getLine >>=
- (inpStr -> putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!")
复制代码
do 语法糖的四条转换规则:
1. do {e} --> e
2. do {e; es} --> e >> do {es}
3. do {let decls; es} --> let decls in do {es}
4. do {p <- e; es} -> let ok p = do {es} ; ok _ = fail "..." in e >> ok
还有几个没有提到的,请参阅回帖
[ 本帖最后由 izhier 于 2009-5-1 19:10 编辑 ]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这个是 Haskell 的类型系统中让我觉得最别扭的地方
我不是用这个方法测试的。
我是
复制代码这样子的。
发现 GHCi 支持的好多啊。看来都是骗人的。
是这样的:
复制代码
变成
复制代码
嗯,刚才看了楼上 roy_hu 兄的回复,
觉得用 guard 比用 if 更合适一些,
因为列表同时也是 MonadPlus 的实例。
[ 本帖最后由 flw 于 2009-4-9 12:57 编辑 ]
翻译成Monad:
do
x <- aList
guard (ff x)
return $ fm x
ghc-6.10.1支持的更少:
但是ghc-6.10.2的user's guide里面说Tuples are currently limited to size 100.
想问一下,在 List Comprehensions 中
像
[ fm x | x <- aList, ff x]
这样的怎么转化
难道是:
[ fm x | x <- aList , ff x]
=>
let ok x = if ff x
then [fm x]
else []
in concatMap ok aList
[ 本帖最后由 izhier 于 2009-4-9 11:56 编辑 ]
还有,元组也是语法糖。
我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。
二元组是二元组,三元组是三元组,它们根本就不是同一样东西,
之所以我们平时写七元组八元组都没有什么问题,
是因为 Haskell 环境预定义了很多种元组,
前段时间我试图尝试这个预定义值的上限的时候,失败了。
但是我确实在前几年碰到过这个上限,是一个并不太大的值。
也许是由于 GHC 6.10 增强了也不一定。
补充一下:刚才我在我的 WinHugs 上测试了一下,最多支持 99 元组。换句话说,并不存在“元组”这个类型,只存在 99 个 n 元组类型(1 < n < 100)
Version: WinHugs Sep 2006
[ 本帖最后由 flw 于 2009-4-9 11:51 编辑 ]
其实 -> 也是语法糖。a -> b 和 【data FunctionTakeTypeASpawnTypeB a b】是一个意思。只不过很显然第一种写法更方便,更直观。
:i (->)
关于缩进,既然提到了 Python,我觉得就不得不提到一点:
Haskell 不光有缩进,还有对齐!
事实上,Haskell 的缩进和对齐这一点,导致了 Haskell 程序只能用空格,不能用 TAB。
例子如下:
复制代码
上面这段代码中我使用了空格,而不是 TAB,如果用 TAB,而想让这个程序正确工作,那么就得这么写:
复制代码
这时,问题出现了:【你必须将 tab 键的宽度设置为 8 才能正常阅读此代码!】
如果不是 8 而是 4,就会看到这个效果:
复制代码
而众所周知的是,TAB 宽度是 8 已经用的人越来越少了,现在流行的是 4,
而 Haskell 程序员经常用 2。
综上所述:Haskell 用户应该之用空格做缩进和对齐。
如果这个程序员正巧还在用 Python,那么他必须要在 Python 社区(或公司团队中)提倡空格抵制 TAB,
否则他将不得不面临为这两种语言选择两款不同的编辑器,或者不同的 profile 才行。
虽然像 vim 或者 emacs 这样的超级编辑器是可以为不同的语言采用不同的设置的,
但是许多其它的编辑器并不具备这样的功能。
[ 本帖最后由 flw 于 2009-4-9 12:54 编辑 ]