免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 27194 | 回复: 50
打印 上一主题 下一主题

Haskell 中的语法糖 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-09 11:17 |只看该作者 |倒序浏览
语法糖
下面一段摘自: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. [1, 2, 3, 4] => 1:2:3:4:[]
  2. [1..10] => enumFromTo 1 10
  3. [1, 3..10] => enumFromThenTo 1 3 10
  4. [1..] => enumFrom 1
  5. [1, 3..] => enumFromThen 1 3
复制代码

  1. Prelude> :t enumFrom
  2. enumFrom :: (Enum a) => a -> [a]
  3. Prelude> :t enumFromTo
  4. enumFromTo :: (Enum a) => a -> a -> [a]
  5. Prelude> :t enumFromThen
  6. enumFromThen :: (Enum a) => a -> a -> [a]
  7. Prelude> :t enumFromThenTo
  8. enumFromThenTo :: (Enum a) => a -> a -> a -> [a]

  9. Prelude> enumFromTo 1 10
  10. [1,2,3,4,5,6,7,8,9,10]
  11. Prelude> enumFromThenTo 1 3 10
  12. [1,3,5,7,9]
  13. Prelude> enumFrom 1
  14. [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
  15. ...
  16. ...
  17. 15732,15733,15Interrupted.
复制代码

3.String
String 其实也是 List,之所以把它单独列出来,是因为 String 和 List 写法不一样,前者被双引号括住,而后者被中括号括住
String 的最终转换如下:

  1. "abcde" => 'a':'b':'c':'d':'e':[]
复制代码

4.List Comprehensions
List Comprehensions 等同于 map, filter 函数,但比 map, filter 函数更直观。
List Comprehensions 的最终转换如下:

  1. [ x | x <- aList , f x]
  2. =>
  3. let ok x = if f x
  4.              then [x]
  5.              else []
  6. in concatMap ok aList
复制代码

  1. Prelude> :t concatMap
  2. concatMap :: (a -> [b]) -> [a] -> [b]
复制代码

5.Section
Section 最终会转换为 lambda 函数
例:

  1. (+2)
  2. => \x -> x+2

  3. (*2)
  4. => \x -> x*2
复制代码

6. infix/prefix

  1. 1 + 2 => (+) 1 2
  2. take 1 [0, 1] => 1 `take` [0, 1]
复制代码


7. Named Fields

  1. data Configuration =
  2.   Configuration { username        :: String,
  3.                        localhost        :: String,
  4.                       remotehost        :: String,
  5.                       isguest        :: Bool,
  6.                       issuperuser        :: Bool,
  7.                       currentdir        :: String,
  8.                       homedir        :: String,
  9.                      timeconnected        :: Integer
  10.                 }

  11. 会转换为:

  12. data Configuration =
  13.   Configuration String                -- user name
  14.                     String                -- local host
  15.                     String                -- remote host
  16.                     Bool                -- is guest?
  17.                     Bool                -- is super user?
  18.                     String                -- current directory
  19.                     String                -- home directory
  20.                    Integer                -- time connected
  21.                 deriving (Eq, Show)

  22. username :: Configuration -> String
  23. userName (Configuration un _ _ _ _ _ _ _) = un

  24. localhost :: Configuration -> String
  25. localHost (Configuration _ lh _ _ _ _ _ _) = lh

  26. remoteHost :: Configuration -> String
  27. remoteHost (Configuration _ _ rh _ _ _ _ _) = rh

  28. isGuest :: Configuration -> Bool
  29. isGuest (Configuration _ _ _ ig _ _ _ _) = ig

  30. ...
复制代码

  1. hostData (Configuration {localhost=lh,remotehost=rh}) = (lh,rh)

  2. 会转换为:

  3. hostData (Configuration _ lh rh _ _ _ _ _) = (lh,rh)
复制代码

  1. initCFG' =
  2.   Configuration
  3.     { username="nobody",
  4.       localhost="nowhere",
  5.       remotehost="nowhere",
  6.       isguest=False,
  7.       issuperuser=False,
  8.       currentdir="/",
  9.       homedir="/",
  10.       timeconnected=0 }

  11. 会转换为:

  12. initCFG = Configuration "nobody" "nowhere" "nowhere" False False "/" "/" 0
复制代码

8.do
do 也是语法糖

  1. main = do
  2.    putStrLn "Greetings! What is your name?"
  3.    inpStr <- getLine
  4.    putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
复制代码

=>

  1. main =
  2.    putStrLn "Greetings! What is your name?" >>
  3.    getLine >>=
  4.    (\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 编辑 ]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
2 [报告]
发表于 2009-04-09 11:36 |只看该作者
关于缩进,既然提到了 Python,我觉得就不得不提到一点:
Haskell 不光有缩进,还有对齐!

事实上,Haskell 的缩进和对齐这一点,导致了 Haskell 程序只能用空格,不能用 TAB。

例子如下:
  1. D:\MoChou>cat ttt.hs
  2. main = do   print "hello"
  3.             print "world"

  4. D:\MoChou>runhaskell ttt.hs
  5. "hello"
  6. "world"

  7. D:\MoChou>
复制代码

上面这段代码中我使用了空格,而不是 TAB,如果用 TAB,而想让这个程序正确工作,那么就得这么写:
  1. main = do<tab>print "hello"
  2. <tab><tab>print "world"
复制代码

这时,问题出现了:【你必须将 tab 键的宽度设置为 8 才能正常阅读此代码!】
如果不是 8 而是 4,就会看到这个效果:
  1. main = do   print "hello"
  2.         print "world"
复制代码

而众所周知的是,TAB 宽度是 8 已经用的人越来越少了,现在流行的是 4,
而 Haskell 程序员经常用 2。

综上所述:Haskell 用户应该之用空格做缩进和对齐。
如果这个程序员正巧还在用 Python,那么他必须要在 Python 社区(或公司团队中)提倡空格抵制 TAB,
否则他将不得不面临为这两种语言选择两款不同的编辑器,或者不同的 profile 才行。

虽然像 vim 或者 emacs 这样的超级编辑器是可以为不同的语言采用不同的设置的,
但是许多其它的编辑器并不具备这样的功能。

[ 本帖最后由 flw 于 2009-4-9 12:54 编辑 ]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
3 [报告]
发表于 2009-04-09 11:41 |只看该作者
其实 -> 也是语法糖。a -> b 和 【data FunctionTakeTypeASpawnTypeB a b】是一个意思。只不过很显然第一种写法更方便,更直观。

:i (->)

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2009-04-09 11:48 |只看该作者
还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组,它们根本就不是同一样东西,
之所以我们平时写七元组八元组都没有什么问题,
是因为 Haskell 环境预定义了很多种元组,
前段时间我试图尝试这个预定义值的上限的时候,失败了。
但是我确实在前几年碰到过这个上限,是一个并不太大的值。
也许是由于 GHC 6.10 增强了也不一定。

补充一下:刚才我在我的 WinHugs 上测试了一下,最多支持 99 元组。换句话说,并不存在“元组”这个类型,只存在 99 个 n 元组类型(1 < n < 100)
Version: WinHugs Sep 2006

[ 本帖最后由 flw 于 2009-4-9 11:51 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2009-04-09 11:51 |只看该作者
想问一下,在 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 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2009-04-09 12:36 |只看该作者
原帖由 flw 于 2009-4-9 11:48 发表
还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组, ...

ghc-6.10.1支持的更少:
*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

但是ghc-6.10.2的user's guide里面说Tuples are currently limited to size 100.

论坛徽章:
0
7 [报告]
发表于 2009-04-09 12:40 |只看该作者
原帖由 izhier 于 2009-4-9 11:51 发表
想问一下,在 List Comprehensions 中

[ fm x | x  


翻译成Monad:
do
  x <- aList
  guard (ff x)
  return $ fm x

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
8 [报告]
发表于 2009-04-09 12:43 |只看该作者
是这样的:

  1. foo = [ fm x | x <- aList , ff x]
复制代码


变成

  1. foo = do
  2.     x <- aList
  3.     if ff x
  4.         then return (fm x)
  5.         else fail "no message"
复制代码



嗯,刚才看了楼上 roy_hu 兄的回复,
觉得用 guard 比用 if 更合适一些,
因为列表同时也是 MonadPlus 的实例。

[ 本帖最后由 flw 于 2009-4-9 12:57 编辑 ]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
9 [报告]
发表于 2009-04-09 12:51 |只看该作者
原帖由 roy_hu 于 2009-4-9 12:36 发表

ghc-6.10.1支持的更少:
*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, ...

我不是用这个方法测试的。
我是
  1. :t (,,,,,,,,,,,,,,,,,,,)
复制代码
这样子的。
发现 GHCi 支持的好多啊。看来都是骗人的。

论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
10 [报告]
发表于 2009-04-09 13:58 |只看该作者
原帖由 flw 于 2009-4-9 11:48 发表

还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组,它们根本就不是同一样东西,

这个是 Haskell 的类型系统中让我觉得最别扭的地方
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP