我将教授离散结构的低年级课程。我已选好课本 离散结构、逻辑和可计算性 部分原因是它包含有利于使用函数式编程语言实现的示例和概念。(我也认为这是一本很好的教科书。)

我想要一种易于理解的 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 的隐式柯里化语法有点迟钝,但并非完全陌生。例如:富::整数 -> 整数 -> 整数。
  • Haskell 默认使用任意精度整数。它是 SML/NJ 中的外部库。默认情况下,SML/NJ 将输出截断为 70 个字符。
  • Haskell 的 lambda 语法很微妙——它使用单个反斜杠。SML 更加明确。不过,不确定我们在这个类中是否需要 lambda。

本质上,SML 和 Haskell 大致是等价的。我倾向于 Haskell,因为我喜欢 Haskell 中的列表推导式和无限列表。但我担心 Haskell 紧凑语法中的大量符号可能会给学生带来问题。根据我阅读有关 SO 的其他文章所收集的信息,不建议初学者从 FP 开始使用 Haskell。但我们不会构建成熟的应用程序,只是尝试简单的算法。

你怎么认为?


编辑:在阅读了您的一些精彩回复后,我应该澄清我的一些要点。

在 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.)

  • 无限的数据结构让人们大吃一惊。对于初学者来说,最好让他们定义一个包含引用单元和 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 令人困惑。

关于相关主题,以下是一些针对您的课程准备的建议:不要忽视 纯函数式数据结构 作者:克里斯·冈崎。即使您不让您的学生使用它,您也肯定会想要一份副本。

其他提示

我们向大学一年级学生教授 Haskell。我对此的感受有点复杂。一方面,向一年级学生教授 Haskell 意味着他们不必忘记命令式风格。Haskell 还可以生成非常简洁的代码,以前学过 Java 的人都可以欣赏。

我注意到学生经常遇到的一些问题:

  • 一开始,模式匹配可能有点困难。学生们最初在理解价值构建和模式匹配之间的关系时遇到了一些问题。他们在区分抽象之间也存在一些问题。我们的练习包括编写简化算术表达式的函数,一些学生很难看出抽象表示之间的区别(例如, Const 1)和元语言表示(1).

    此外,如果您的学生应该自己编写列表处理函数,请小心指出模式之间的差异

    []
    [x]
    (x:xs)
    [x:xs]
    

    根据您想教他们多少函数式编程,您可以只给他们一些库函数并让他们使用。

  • 我们没有教学生有关匿名函数的知识,我们只是告诉他们 where 条款。对于某些任务来说,这有点冗长,但在其他方面效果很好。我们也没有告诉他们有关部分申请的信息;这在 Haskell 中可能很容易解释(由于其书写类型的形式),因此可能值得向他们展示。

  • 他们很快发现了列表推导式,并且比高阶函数(例如 filter, map, zipWith.

  • 我认为我们错过了一些教导他们如何让他们通过类型来引导他们的想法的机会。不过,我不太确定这对初学者是否有帮助。

  • 错误消息通常对初学者没有太大帮助,他们可能偶尔需要一些帮助。我自己没有尝试过,但是有一个专门针对新手的Haskell编译器,主要是通过更好的错误消息来实现:

  • 对于小程序来说,可能的空间泄漏之类的事情不是问题。

总的来说,Haskell 是一种很好的教学语言,但也存在一些缺陷。鉴于学生对列表推导式感觉比高阶函数更舒服,这可能就是您需要的论据。我不知道你的课程有多长,也不知道你想教他们多少编程,但一定要计划一些时间来教他们基本概念——他们会需要它。

顺便提一句,

#SML具有真正的交互式解释器,其中既可以定义又可以使用函数。在Haskell中,必须在单独的文件中定义函数并在交互式外壳中使用之前进行编译。

不准确。使用 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 作为第一门函数式语言甚至第一门编程语言来教授,所以我认为这不会成为问题。

在对这样一门课程进行过一些教学之后,我不同意您所发现的可能存在的困惑有那么可能。早期混乱最可能的来源是由错误布局引起的解析错误,以及错误使用数字文字时有关类型类的神秘消息。

我也不同意任何关于不建议初学者使用 FP 的建议。这当然是一种大爆炸方法,而具有突变的严格语言则不然,但我认为这是一种非常有效的方法。

  • SML 有一个真正的交互式解释器,可以在其中定义和使用函数。在 Haskell 中,函数必须在单独的文件中定义并在交互式 shell 中使用之前进行编译。

虽然 Hugs 可能有这样的限制,但 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 的隐式柯里化语法有点迟钝,但并非完全陌生。例如:富::整数 -> 整数 -> 整数。

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 的其他文章所收集的信息,不建议初学者从 FP 开始使用 Haskell。但我们不会构建成熟的应用程序,只是尝试简单的算法。

与 SML 相比,我更喜欢使用 Haskell,但我仍然会先教 SML。

  • 支持诺米诺洛的想法,列出理解 似乎会减慢学生学习某些高阶函数的速度。
  • 如果您想要惰性和无限列表,那么明确地实现它是有启发性的。
  • 因为 SML 是热切评估的,所以执行模型更容易理解,并且“通过 printf 进行调试”比 Haskell 中的效果要好得多。
  • SML 的类型系统也更简单。虽然你的类可能无论如何都不会使用它们,但 Haskell 的类型类仍然是一个需要克服的额外障碍——让他们理解 'a 相对 ''a SML 中的区别已经够难的了。

大多数答案都是技术性的,但我认为您至少应该考虑一个非技术性的答案:Haskell(即 OCaml)目前拥有更大的社区,在更广泛的环境中使用它。还有一个大型数据库,其中包含为盈利和娱乐而编写的库和应用程序 哈奇奇. 。这可能是让你的一些学生在课程结束后继续使用该语言的一个重要因素,并且可能会在以后尝试其他函数式语言(例如标准机器学习)。

我很惊讶您没有考虑 OCaml 和 F#,因为它们解决了您的许多担忧。体面且有益的发展环境肯定是学习者的首要任务吗?在这方面,SML 远远落后,而 F# 则远远领先于所有其他 FPL。

此外,OCaml 和 F# 都具有列表推导式。

哈斯克尔。由于我从使用 Haskell 中学到的东西,我在计算机科学的算法/理论课上取得了领先。这是一门非常全面的语言,它会教你大量的计算机科学, 只要使用它.

然而,SML 更容易学习。Haskell 具有惰性求值和控制结构等功能,使其更加强大,但代价是学习曲线陡峭。SML没有这样的曲线。

也就是说,大多数 Haskell 都放弃了从 Ruby、ObjC 或 Python 等不太科学/数学的语言中学习的东西。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top