免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: izhier
打印 上一主题 下一主题

haskell 初学问疑 [复制链接]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
1 [报告]
发表于 2009-03-27 00:14 |显示全部楼层
Haskell 可以任意制造新的运算符,
因此在做词法分析时,依赖空格来分隔 token 应该可以理解。
其实说来也怪,我学别的语言的时候,都会去学习它的词法分析过程,
但是学 Haskell 从来没关心过这个。

至于递归麽,递归有一种叫做尾递归,编译期可以优化成循环的,
不是尾递归的,也有一部分可以转化成尾递归,这个理论上编译期都可以做优化。
剩下那些不能优化的,递归的时候就会影响性能,尤其是栈的消耗,
不过栈有大小限制,超过大小就会出错。

至于 Python 的生成器么,绝对不是 lazy,因为它的语义就是那样,语义和实际的行为是吻合的。
而 Haskell 的 lazy 是指,语义和行为不吻合。
比如 [1..],它的语义是个无限列表,但是实际使用它的时候,比如 take 10 [1..],并没有真的产生这个无限列表。
再比如 readFile "1G.file",它的语义是把整个 1G 文件的内容都返回,
但是实际运行的时候,它并不会真的读 1G,也是需要多少就读多少。
这才是 lazy 的意思:语义承诺很多,但是实际做到刚好。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
2 [报告]
发表于 2009-03-27 10:57 |显示全部楼层
原帖由 izhier 于 2009-3-27 10:53 发表


python generator 也可以由此功能呀!

>>> def gen():
...     n = 0
...     while True:
...             yield n
...             n += 1
...
>>> g = gen()
>>> g.next()
0
>>> g.next()
...

yield 的语义,你真的明白了吗?

yield 是一个流程控制语句,它的语义明明确确,除了保存函数调用栈之外,我想不出它和 return 有什么分别。

[ 本帖最后由 flw 于 2009-3-27 10:59 编辑 ]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
3 [报告]
发表于 2009-03-27 11:00 |显示全部楼层
原帖由 izhier 于 2009-3-27 10:58 发表

python 中读文件时:

f = open("file.txt", 'r')
for each_line in f:
    do_something

以上也是一句一句的读文件,并不是一次性的把文件读完呀

注意语义。
f 是一个文件句柄,
而 readFile 返回的是一个字符串,且从语义上讲,这个字符串就是文件的所有内容。

我再强调一次,一定要搞清楚语义。
Python 的确可以一行一行的读文件,这很好。大多数语言都支持这个功能。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2009-03-27 11:05 |显示全部楼层
原帖由 izhier 于 2009-3-27 11:03 发表

不是太明白你说的语义是什么?

generator 不是只有在需要返回值时,才再次运行函数吗?
难道这没有 lazy 吗?
是不是我对 lazy 的概念有点不清呀?

慢慢来吧。别着急。时间长就好了。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2009-03-27 11:25 |显示全部楼层
每个语句,或者每个函数,都有它的语义。
语义搞懂这是个讨论的前提。

我前面已经说了,Haskell 的 lazy 体现在:
语义承诺了很多,不一定真的会去做那么多,实际上只要做到刚好就行了
这就叫惰性求值。

yield 的语义,就是立即停止本函数的运行,并且当前的调用栈打包到一个生成器对象中,并且 return 这个生成器对象
你可以清清楚楚地看到,yield 根本就没承诺会继续运行——除非你调用了 yield 所返回的生成器的 next 方法。
但调用 next 方法之后所导致的行为这又是 next 方法的语义了,它的语义和行为始终是是一致的。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2009-03-27 12:13 |显示全部楼层
原帖由 izhier 于 2009-3-27 12:09 发表
还有一个问题,也是关于递归的

haskell 对递归进行优化,从而程序员可以大量使用递归
而 imperative 语言中不提倡运用递归,因为递归会消耗大量的资源

为什么 imperative 语言不对递归进行像 haskell 那 ...

不方便也许也是有的。毕竟有那么多的变量。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
7 [报告]
发表于 2009-03-27 15:56 |显示全部楼层
原帖由 izhier 于 2009-3-27 15:41 发表
又一个问题::wink:

haskell 中运算符是贪婪性匹配的

haskell 中 function 获取参数也是贪婪性的吧?

应该说,apply(空格)的优先级最高才对。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
8 [报告]
发表于 2009-03-27 18:02 |显示全部楼层
原帖由 izhier 于 2009-3-27 17:44 发表
又来了

Hugs.Base> :t sqrt
sqrt :: Floating a => a -> a
Hugs.Base> (sqrt 2)::Float
1.414214
Hugs.Base> sqrt (2::Float)
1.414214
Hugs.Base> sqrt 2::Float
1.414214

以上优先级怎 ...

1 和 3 是相同的。
2 和它们两个不同。

由于字面值 2 实际上被定义成了 Num 类的任何一个实例的类型,
而 Float 是 Num 类的实例,因此 2 是可以转化成 Float 类型的。
在 1 3 两个情形中,
由于 sqrt 2 的结果被描绘成了 Float,因此由它的原型 a -> a 可以推导出它的参数必然也是 Float,
而 2 正好也满足了这个条件,因此类型匹配,计算得以继续,得到了 2 的平方根。
在第 2 个情形中,
由于 sqrt 的参数被描绘成了 Float 型,因此 sqrt 2 的结果自然也是 Float 型。

其实用 show (2::Int) 就可以很容易看到 :: 和 apply 的词法优先级谁高谁低。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
9 [报告]
发表于 2009-03-27 18:34 |显示全部楼层
原帖由 izhier 于 2009-3-27 18:07 发表
原来如此!

再问一下:

Hugs.Base> sqrt 2::Float
1.414214
Hugs.Base> sqrt 2
1.4142135623731

以上两个表达式的为什么不同 ?
难道类型不一样 ?

Hugs.Base> :t (sqrt 2)
sqrt 2 :: Floating  ...

Floating 是类 class
Float 是类型 type

Float 是 Floating 的实例,
由于 sqrt 的类型是 Floating a => a -> a,所以实际上是可以匹配 Float -> Float 的。

由 :i Floating 可以看到,Floating 还有一个实例叫做 Double。
估计 sqrt 2 是按照 sqrt 2::Double 来处理了。这个就是编译器的缺省规则了,注意观察并且记住即可。

[ 本帖最后由 flw 于 2009-3-27 18:42 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP