我将在离散结构中教授低级课程.我选择了教科书" 离散结构,逻辑和可计算性",因为它包含有助于使用函数式编程语言实现的示例和概念.(我也认为这是一本很好的教科书.)
我想要一个易于理解的FP语言来说明DS概念以及学生可以使用的.大多数学生最多只能用一到两个学期的Java编程.在查看Scheme,Erlang,Haskell,Ocaml和SML之后,我已经确定了Haskell或Standard ML.由于下面列出的原因,我倾向于Haskell,但我喜欢那些活跃的程序员在一个或另一个的意见.
Haskell和SML都具有模式匹配,这使得描述递归算法变得简单.
Haskell具有很好的列表推导,可以很好地匹配这些列表以数学方式表达的方式.
Haskell有懒惰的评价.非常适合使用列表推导技术构建无限列表.
SML有一个真正的交互式解释器,可以在其中定义和使用函数.在Haskell中,函数必须在单独的文件中定义,并在交互式shell中使用之前进行编译.
SML以易于理解的语法明确确认函数参数和返回类型.例如:val foo = fn:int*int - > int.Haskell隐含的咖喱语法有点迟钝,但并非完全陌生.例如:foo :: Int - > Int - > Int.
Haskell默认使用任意精度的整数.它是SML/NJ中的外部库.并且SML/NJ默认将输出截断为70个字符.
Haskell的lambda语法很微妙 - 它使用单个反斜杠.SML更明确.但是不确定我们是否会在这堂课中需要lambda.
从本质上讲,SML和Haskell大致相同.我倾向于Haskell,因为我喜欢Haskell中的列表理解和无限列表.但我担心Haskell紧凑语法中的大量符号可能会导致学生出现问题.从我收集到的关于SO的其他帖子开始,Haskell不建议初学者从FP开始.但我们不打算构建成熟的应用程序,只是尝试简单的算法.
你怎么看?
编辑:在阅读了一些很棒的回复后,我应该澄清一些我的要点.
在SML中,在解释器中定义函数和在外部文件中定义函数之间没有语法上的区别.假设您要编写阶乘函数.在Haskell中,您可以将此定义放入文件中并将其加载到GHCi中:
fac 0 = 1 fac n = n * fac (n-1)
对我来说,这很清楚,简洁,并且符合书中的数学定义.但是如果你想直接在GHCi中编写函数,你必须使用不同的语法:
let fac 0 = 1; fac n = n * fac (n-1)
在使用交互式口译员时,从教学角度来看,当学生在文件和命令行中使用相同的代码时,非常非常方便.
通过"显式确认函数",我的意思是在定义函数时,SML立即告诉您函数的名称,参数的类型和返回类型.在Haskell中你必须使用:type
命令,然后你会得到一些有点令人困惑的咖喱符号.
关于Haskell的一个更酷的事情 - 这是一个有效的函数定义:
fac 0 = 1 fac (n+1) = (n+1) * fac n
同样,这与他们可能在教科书中找到的定义相匹配.在SML中无法做到这一点!
就像我喜欢Haskell一样,以下是我更喜欢SML用于离散数学和数据结构(以及大多数其他初学者类)的类的原因:
即使对于专家来说,Haskell程序的时间和空间成本也很难预测.SML提供了更有限的方式来吹制机器.
交互式解释器中的函数定义语法与文件中使用的语法相同,因此您可以剪切和粘贴.
虽然SML中的运算符重载完全是假的,但它也很简单.在不必进入类型类的情况下,很难在Haskell中教授整个类.
学生可以使用调试print
.(虽然,正如评论者指出的那样,在Haskell中使用它可能会得到几乎相同的效果Debug.Trace.trace
.)
无限的数据结构打动了人们的思想.对于初学者来说,最好让他们定义一个带有ref单元格和thunk的流类型,这样他们就知道它是如何工作的:
datatype 'a thunk_contents = UNEVALUATED of unit -> 'a | VALUE of 'a type 'a thunk = 'a thunk_contents ref val delay : (unit -> 'a) -> 'a thunk val force : 'a thunk -> 'a
现在它不再是魔术了,你可以从这里到流(无限列表).
布局不像Python那么简单,可能会令人困惑.
Haskell有两个优势:
在核心Haskell中,您可以在定义之前编写函数的类型签名.这对学生和其他初学者非常有帮助.在SML中处理类型签名并不是一种好方法.
Haskell具有更好的具体语法.Haskell语法是对ML语法的重大改进.我写了一篇关于何时在ML程序中使用括号的简短说明 ; 这有点帮助.
最后,有一把剑可以削减两种方式:
Haskell代码默认是纯粹的,所以你的学生不太可能偶然发现不纯的结构(IO monad,state monad).但出于同样的原因,它们无法打印,如果你想做I/O,那么最低限度你必须解释do
符号,并且return
令人困惑.
在相关主题上,这里有一些关于课程准备的建议:不要忽视Chris Okasaki的Purely Functional Data Structures.即使你没有让你的学生使用它,你一定会想要一份副本.
我们在大学的第一年教Haskell.我对此的看法有点混乱.一方面教Haskell到第一年意味着他们不必忘记命令式的风格.Haskell也可以生成非常简洁的代码,以前有过Java的人可以欣赏它们.
我注意到学生经常遇到的一些问题:
首先,模式匹配可能有点困难.学生最初在看到价值构建和模式匹配是如何相关时遇到了一些问题.他们在区分抽象方面也存在一些问题.我们的练习包括编写简化算术表达的函数,有些学生很难看到抽象表示(例如Const 1
)和元语言表示(1
)之间的区别.
此外,如果您的学生应该自己编写列表处理功能,请注意指出模式之间的差异
[] [x] (x:xs) [x:xs]
根据您想要在途中教授多少函数式编程,您可以给它们一些库函数,让它们随意使用它们.
我们没有教我们的学生匿名功能,我们只是告诉他们有关where
条款.对于某些任务,这有点冗长,但其他方面效果不错.我们也没有告诉他们部分申请; 这可能很容易在Haskell中解释(由于它的编写类型),所以它可能值得向他们展示.
他们很快就发现列表理解和首选他们在高阶功能,如filter
,map
,zipWith
.
我想我们错过了教他们如何让他们按类型引导他们的想法.不过,我不太确定这对初学者是否有帮助.
错误消息通常对初学者没有太大帮助,他们可能偶尔需要一些帮助.我自己没有尝试过,但是有一个专门针对新手的Haskell编译器,主要是通过更好的错误消息:Helium
对于小型程序,可能的空间泄漏等问题不是问题.
总的来说,Haskell是一种很好的教学语言,但有一些陷阱.鉴于学生对列表推导感觉比高阶函数更舒服,这可能是您需要的论据.我不知道你的课程有多长,或者你想教他们多少程序,但是要花些时间教他们基本概念 - 他们需要它.
顺便说一句,
#SML有一个真正的交互式解释器,可以在其中定义和使用函数.在Haskell中,函数必须在单独的文件中定义,并在交互式shell中使用之前进行编译.
不准确.使用GHCi:
Prelude> let f x = x ^ 2 Prelude> f 7 49 Prelude> f 2 4
在haskell.org edu上,Haskell在教育方面也有很好的资源.页面,有不同老师的经验.http://haskell.org/haskellwiki/Haskell_in_education
最后,如果你使用Haskell,你将能够教他们多核并行,只是为了好玩:-)
许多大学教Haskell作为第一种功能语言甚至是第一种编程语言,所以我认为这不会是一个问题.
在一门这样的课程上完成了一些教学,我不同意你认识到的可能的混淆.早期混淆的最可能的来源是解析由错误布局引起的错误,以及在错误地使用数字文字时关于类型类的神秘消息.
我也不同意任何关于Haskell不建议初学者开始使用FP的建议.对于突变的严格语言来说,这肯定是一个大爆炸的方法,但我认为这是一种非常有效的方法.
SML有一个真正的交互式解释器,可以在其中定义和使用函数.在Haskell中,函数必须在单独的文件中定义,并在交互式shell中使用之前进行编译.
虽然拥抱可能有这种限制,但GHCi不会:
$ ghci GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. Prelude> let hello name = "Hello, " ++ name Prelude> hello "Barry" "Hello, Barry"
我喜欢GHC(i)而不是Hugs的原因有很多,这只是其中之一.
SML以易于理解的语法明确确认函数参数和返回类型.例如:val foo = fn:int*int - > int.Haskell隐含的咖喱语法有点迟钝,但并非完全陌生.例如:foo :: Int - > Int - > Int.
SML也有你所谓的"隐式咖喱"语法.
$ sml Standard ML of New Jersey v110.69 [built: Fri Mar 13 16:02:47 2009] - fun add x y = x + y; val add = fn : int -> int -> int
从本质上讲,SML和Haskell大致相同.我倾向于Haskell,因为我喜欢Haskell中的列表理解和无限列表.但我担心Haskell紧凑语法中的大量符号可能会导致学生出现问题.从我收集到的关于SO的其他帖子开始,Haskell不建议初学者从FP开始.但我们不打算构建成熟的应用程序,只是尝试简单的算法.
我喜欢使用Haskell比SML更多,但我仍然会先教SML.
借调nominolo的想法,列表内涵也似乎从得到一些高阶函数慢的学生.
如果你想要懒惰和无限列表,明确地实现它是有益的.
因为热切地评估了SML,所以执行模型更容易理解,并且"通过printf调试"比Haskell更好地工作.
SML的类型系统也更简单.当你的类可能不会反正使用它们,Haskell的类型类仍然是一个多余的突起拿到过-让他们了解'a
与''a
在SML的区别是不够强硬.
大多数答案都是技术性的,但我认为你应该考虑至少一个不是:Haskell(作为OCaml),在这个时候,有一个更大的社区在更广泛的背景下使用它.还有一个大型的图书馆和应用程序数据库,为Hackage的利润和乐趣而编写.这可能是让您的学生在课程结束后使用该语言的一个重要因素,也许可以在以后尝试其他功能语言(如标准ML).