Haskell 中的语法糖
2011-02-27 18:04
411 查看
在Wiki里关于语法糖的一些历史介绍:
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)
创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜“的语法。语法糖往往给程序员提供了更实用的
编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
Wiki里提到,在C语言里用a
表示*(a+n),用a
[m]表示*(*(a+n)+m),也是语法糖。
实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,
但是C++更进一步的推广了这种风格,更好用了。
按照Wiki的理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。[/color]
现在我们介绍一下 Haskell 中的语法糖,[color=Blue]如有错误,请指正,改之[/color]
[color=Red]1.缩进[/color]
Haskell 也像 C 语言一样可以用大括号 '{}' 和分号 ';' 来构造程序块,但这样的写法会造成程序难以阅读,为了解决这个问题,Haskell 引入了缩进。
注意 Haskell 的缩进不同于 Python 的缩进。在 Python 中,tab/空格 是语法,而 Haskell 中 tab/空格 是定义良好的语法糖,最终会被转换成
括号和分号的程序块的语法。
使用缩进的结构有:let,where,case of,... ? (其他的就不了解了)。在它们之后的缩进都可以转换为括号和分号的程序块语法。
[color=Red]2.Section[/color]
Section 最终会转换为 lambda 函数
例:
[color=Red]3.List[/color]
List 的最终转换如下:
[color=Red]4.String[/color]
String 其实也是 List,之所以把它单独列出来,是因为 String 和 List 写法不一样,前者被双引号括住,而后者被中括号括住
String 的最终转换如下:
[color=Red]5.List Comprehensions[/color]
List Comprehensions 等同于 map, filter 函数,但比 map, filter 函数更直观。
List Comprehensions 的最终转换如下:
[b]
[color=Red]6.do[/color]
do 也是语法糖,但现在对 monad 还完全不了解,就不说了,如果以后有机会再补上。
[color=Red]7. infix/prefix[/color]
引用 Magicloud 的原话
1 + 2 等效于 (+) 1 2
take 1 [0, 1] 等效于 1 `take` [0, 1]
无论是 (+) 还是 `take`,其目的是代码的美观易读,对于程序逻辑没有丝毫影响。
[color=Red]8. Named Fields[/color]
[/b]
[b]
[color=Blue]还有几个没有提到的,请参阅回帖[/color]
[ 本帖最后由 izhier 于 2009-4-19 14:29 编辑 ]
[/b]
[b][b] flw 回复于:2009-04-09 11:36:30
关于缩进,既然提到了 Python,我觉得就不得不提到一点:
[color=red]Haskell 不光有缩进,还有对齐![/color]
事实上,Haskell 的缩进和对齐这一点,导致了 [color=red]Haskell 程序只能用空格,不能用 TAB。[/color]
例子如下:
[font=新宋体][/b] [/b]
[b][/font]
上面这段代码中我使用了空格,而不是 TAB,如果用 TAB,而想让这个程序正确工作,那么就得这么写:
[font=新宋体] [/b]
[b][/font]
这时,问题出现了:【你必须将 tab 键的宽度设置为 8 才能正常阅读此代码!】
如果不是 8 而是 4,就会看到这个效果:
[font=新宋体] [/b]
[b][/font]
而众所周知的是,TAB 宽度是 8 已经用的人越来越少了,现在流行的是 4,
而 Haskell 程序员经常用 2。
综上所述:Haskell 用户应该之用空格做缩进和对齐。
如果这个程序员正巧还在用 Python,那么他必须要在 Python 社区(或公司团队中)提倡空格抵制 TAB,
否则他将不得不面临为这两种语言选择两款不同的编辑器,或者不同的 profile 才行。
虽然像 vim 或者 emacs 这样的超级编辑器是可以为不同的语言采用不同的设置的,
但是许多其它的编辑器并不具备这样的功能。
[ 本帖最后由 flw 于 2009-4-9 12:54 编辑 ]
[/b]
[b][b] flw 回复于:2009-04-09 11:41:24
其实 -> 也是语法糖。a -> b 和 【data FunctionTakeTypeASpawnTypeB a b】是一个意思。只不过很显然第一种写法更方便,更直观。
:i (->)
[/b][/b]
[b][b] flw 回复于:2009-04-09 11:48:19
还有,元组也是语法糖。
我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。
二元组是二元组,三元组是三元组,它们根本就不是同一样东西,
之所以我们平时写七元组八元组都没有什么问题,
是因为 Haskell 环境预定义了很多种元组,
前段时间我试图尝试这个预定义值的上限的时候,失败了。
但是我确实在前几年碰到过这个上限,是一个并不太大的值。
也许是由于 GHC 6.10 增强了也不一定。
补充一下:刚才我在我的 WinHugs 上测试了一下,最多支持 99 元组。换句话说,并不存在“元组”这个类型,只存在 99 个 n 元组类型(1 < n < 100)
Version: WinHugs Sep 2006
[ 本帖最后由 flw 于 2009-4-9 11:51 编辑 ]
[/b][/b]
[b][b] izhier 回复于:2009-04-09 11:51:14
想问一下,在 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 编辑 ]
[/b][/b]
[b][b] roy_hu 回复于:2009-04-09 12:36:14
引用:原帖由 flw 于 2009-4-9 11:48 发表
还有,元组也是语法糖。
我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。
二元组是二元组,三元组是三元组, ...
ghc-6.10.1支持的更少:
[indent]*Main> (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,3
8,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,7
5,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100)
<interactive>:1:0:
A 100-tuple is too large for GHC
(max size is 62)
Workaround: use nested tuples or define a data type[/indent]
但是ghc-6.10.2的[url=http://www.haskell.org/ghc/docs/latest/html /users_guide/bugs-and-infelicities.html]user's guide里面说 Tuples are currently limited to size 100.
[/b][/b]
[b][b] [url=http://bbs.chinaunix.net/viewpro.php?uid=12753412]roy_hu 回复于:2009-04-09 12:40:35
引用:原帖由 izhier 于 2009-4-9 11:51 发表
想问一下,在 List Comprehensions 中
像
[ fm x | x
翻译成Monad:
do
x <- aList
guard (ff x)
return $ fm x
[/b][/b]
[b][b] [url=http://bbs.chinaunix.net/viewpro.php?uid=20014893]flw 回复于:2009-04-09 12:43:02
是这样的:
[/b][/b]
[b]
变成
[/b]
[b]
嗯,刚才看了楼上 roy_hu 兄的回复,
觉得用 guard 比用 if 更合适一些,
因为列表同时也是 MonadPlus 的实例。
[ 本帖最后由 flw 于 2009-4-9 12:57 编辑 ] [/b]
[b]http://www.chinaunix.net/jh/142/1425290.html [/b]
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)
创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜“的语法。语法糖往往给程序员提供了更实用的
编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
Wiki里提到,在C语言里用a
表示*(a+n),用a
[m]表示*(*(a+n)+m),也是语法糖。
实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,
但是C++更进一步的推广了这种风格,更好用了。
按照Wiki的理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。[/color]
现在我们介绍一下 Haskell 中的语法糖,[color=Blue]如有错误,请指正,改之[/color]
[color=Red]1.缩进[/color]
Haskell 也像 C 语言一样可以用大括号 '{}' 和分号 ';' 来构造程序块,但这样的写法会造成程序难以阅读,为了解决这个问题,Haskell 引入了缩进。
注意 Haskell 的缩进不同于 Python 的缩进。在 Python 中,tab/空格 是语法,而 Haskell 中 tab/空格 是定义良好的语法糖,最终会被转换成
括号和分号的程序块的语法。
使用缩进的结构有:let,where,case of,... ? (其他的就不了解了)。在它们之后的缩进都可以转换为括号和分号的程序块语法。
[color=Red]2.Section[/color]
Section 最终会转换为 lambda 函数
例:
(+2) => /x -> x+2 (*2) => /x -> x*2
[color=Red]3.List[/color]
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.
[color=Red]4.String[/color]
String 其实也是 List,之所以把它单独列出来,是因为 String 和 List 写法不一样,前者被双引号括住,而后者被中括号括住
String 的最终转换如下:
"abcde" => 'a':'b':'c':'d':'e':[]
[color=Red]5.List Comprehensions[/color]
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 -> ) -> [a] ->
[b]
[color=Red]6.do[/color]
do 也是语法糖,但现在对 monad 还完全不了解,就不说了,如果以后有机会再补上。
[color=Red]7. infix/prefix[/color]
引用 Magicloud 的原话
1 + 2 等效于 (+) 1 2
take 1 [0, 1] 等效于 1 `take` [0, 1]
无论是 (+) 还是 `take`,其目的是代码的美观易读,对于程序逻辑没有丝毫影响。
[color=Red]8. Named Fields[/color]
[/b]
[b] 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 ... [/b]
[b] hostData (Configuration {localhost=lh,remotehost=rh}) = (lh,rh) 会转换为: hostData (Configuration _ lh rh _ _ _ _ _) = (lh,rh) [/b]
[b] initCFG' = Configuration { username="nobody", localhost="nowhere", remotehost="nowhere", isguest=False, issuperuser=False, currentdir="/", homedir="/", timeconnected=0 } 会转换为: initCFG = Configuration "nobody" "nowhere" "nowhere" False False "/" "/" 0 [/b]
[b]
[color=Blue]还有几个没有提到的,请参阅回帖[/color]
[ 本帖最后由 izhier 于 2009-4-19 14:29 编辑 ]
[/b]
[b][b] flw 回复于:2009-04-09 11:36:30
关于缩进,既然提到了 Python,我觉得就不得不提到一点:
[color=red]Haskell 不光有缩进,还有对齐![/color]
事实上,Haskell 的缩进和对齐这一点,导致了 [color=red]Haskell 程序只能用空格,不能用 TAB。[/color]
例子如下:
[font=新宋体][/b] [/b]
[b]D:/MoChou>cat ttt.hs main = do print "hello" print "world" D:/MoChou>runhaskell ttt.hs "hello" "world" D:/MoChou> [/b]
[b][/font]
上面这段代码中我使用了空格,而不是 TAB,如果用 TAB,而想让这个程序正确工作,那么就得这么写:
[font=新宋体] [/b]
[b]main = do<tab>print "hello" <tab><tab>print "world" [/b]
[b][/font]
这时,问题出现了:【你必须将 tab 键的宽度设置为 8 才能正常阅读此代码!】
如果不是 8 而是 4,就会看到这个效果:
[font=新宋体] [/b]
[b]main = do print "hello" print "world" [/b]
[b][/font]
而众所周知的是,TAB 宽度是 8 已经用的人越来越少了,现在流行的是 4,
而 Haskell 程序员经常用 2。
综上所述:Haskell 用户应该之用空格做缩进和对齐。
如果这个程序员正巧还在用 Python,那么他必须要在 Python 社区(或公司团队中)提倡空格抵制 TAB,
否则他将不得不面临为这两种语言选择两款不同的编辑器,或者不同的 profile 才行。
虽然像 vim 或者 emacs 这样的超级编辑器是可以为不同的语言采用不同的设置的,
但是许多其它的编辑器并不具备这样的功能。
[ 本帖最后由 flw 于 2009-4-9 12:54 编辑 ]
[/b]
[b][b] flw 回复于:2009-04-09 11:41:24
其实 -> 也是语法糖。a -> b 和 【data FunctionTakeTypeASpawnTypeB a b】是一个意思。只不过很显然第一种写法更方便,更直观。
:i (->)
[/b][/b]
[b][b] flw 回复于:2009-04-09 11:48:19
还有,元组也是语法糖。
我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。
二元组是二元组,三元组是三元组,它们根本就不是同一样东西,
之所以我们平时写七元组八元组都没有什么问题,
是因为 Haskell 环境预定义了很多种元组,
前段时间我试图尝试这个预定义值的上限的时候,失败了。
但是我确实在前几年碰到过这个上限,是一个并不太大的值。
也许是由于 GHC 6.10 增强了也不一定。
补充一下:刚才我在我的 WinHugs 上测试了一下,最多支持 99 元组。换句话说,并不存在“元组”这个类型,只存在 99 个 n 元组类型(1 < n < 100)
Version: WinHugs Sep 2006
[ 本帖最后由 flw 于 2009-4-9 11:51 编辑 ]
[/b][/b]
[b][b] izhier 回复于:2009-04-09 11:51:14
想问一下,在 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 编辑 ]
[/b][/b]
[b][b] roy_hu 回复于:2009-04-09 12:36:14
引用:原帖由 flw 于 2009-4-9 11:48 发表
还有,元组也是语法糖。
我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。
二元组是二元组,三元组是三元组, ...
ghc-6.10.1支持的更少:
[indent]*Main> (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,3
8,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,7
5,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100)
<interactive>:1:0:
A 100-tuple is too large for GHC
(max size is 62)
Workaround: use nested tuples or define a data type[/indent]
但是ghc-6.10.2的[url=http://www.haskell.org/ghc/docs/latest/html /users_guide/bugs-and-infelicities.html]user's guide里面说 Tuples are currently limited to size 100.
[/b][/b]
[b][b] [url=http://bbs.chinaunix.net/viewpro.php?uid=12753412]roy_hu 回复于:2009-04-09 12:40:35
引用:原帖由 izhier 于 2009-4-9 11:51 发表
想问一下,在 List Comprehensions 中
像
[ fm x | x
翻译成Monad:
do
x <- aList
guard (ff x)
return $ fm x
[/b][/b]
[b][b] [url=http://bbs.chinaunix.net/viewpro.php?uid=20014893]flw 回复于:2009-04-09 12:43:02
是这样的:
[/b][/b]
[b]foo = [ fm x | x <- aList , ff x] [/b]
[b]
变成
[/b]
[b]foo = do x <- aList if ff x then return (fm x) else fail "no message" [/b]
[b]
嗯,刚才看了楼上 roy_hu 兄的回复,
觉得用 guard 比用 if 更合适一些,
因为列表同时也是 MonadPlus 的实例。
[ 本帖最后由 flw 于 2009-4-9 12:57 编辑 ] [/b]
[b]http://www.chinaunix.net/jh/142/1425290.html [/b]
相关文章推荐
- Haskell语法
- Haskell学习笔记 --- 基础语法篇一
- Haskell语法一
- Haskell学习——语法
- haskell基本语法
- 【转载】SQLite基本语法手册(一)
- Android.mk文件语法规范及使用模板
- ASP.NET MVC Razor视图引擎基础语法
- Solr学习之五:Solr查询参数及语法
- [乐意黎原创] php 语法里0不等于null为空的解决办法
- MYSQL语法速查之表更改
- 【Objective-C】06-点语法
- Python基础之一基本语法
- client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法
- php与java语法的区别
- HQL语法结构
- 你学会测试了吗(2):测试语法之属性介绍
- shell编程语法和实例_李强强
- 【C/C++语法外功】传值&传引用&传指针
- PL/SQL语法之游标