23

我仅通过代码大小比较了语言枪战游戏中的语言。这是我得到的摘要(最短的优先,按相似分数分组)。

  1. Python、Ruby、JavaScript、Perl、Lua、PHP、Mozart/OZ
  2. OCaml、Erlang、Racket、Go、Scala、F#、Smalltalk
  3. 帕斯卡、清洁、Haskell、Common Lisp、C#、Java、C
  4. C++、Ada、ATS

我想知道为什么。获胜者似乎是普通的老式动态语言。Erlang、Racket(née PLT Scheme)和 F# 运行良好。Haskell 和 Common Lisp 看起来并不比自称冗长的 Java 更简洁。

更新:

我发现了一篇关于这个主题的有见地的帖子,里面有图表。我还为一个更大的程序(一个简单的光线追踪器)找到了类似的语言比较。总而言之,我不会说我得到了“答案”,但我得到了一些思考。

4

8 回答 8

24

如果函数式语言真的很简洁......

1 - 大型编程不同于小型编程

这些小型基准游戏程序的任何内容都不应作为每种语言提供的抽象和模块化如何应用于大型编程的示例。

2 -您在基准测试游戏摘要页面中看到的大部分内容仅指为每种语言实现贡献的最快程序(较慢的程序通常会在一段时间后从网站上删除 - 何时以及哪些较慢的程序被删除大多是任意的)。

{编辑:亚当,因为您不想相信我的话,摘要页面仅指最快的程序 -查看过滤数据行的脚本以查找“哪种编程语言最好?” 页。查看lib_scorecard.php中函数 ValidRowsAndMins 中的第 80 行和第 82 行- Alioth 颁发自己的安全证书,因此您的浏览器会抱怨。}

因此,以 Haskell 为例,您正在查看已贡献的最快的 Haskell 程序的代码大小。

3 -没有一个流星竞赛程序被删除,并且流星竞赛是一个没有限制的编程竞赛 -最小的流星竞赛 Haskell 程序是最慢的流星竞赛 Haskell 程序

于 2010-08-27T03:06:02.997 回答
19
  1. 没有一种语言总是优于另一种语言(嗯,有一些例外......;)),所以同样适用于一组广泛分类的语言。基准涵盖了广泛的主题,X 可能不如 Y 更适合某个主题。
  2. 源代码是gzip压缩的,我们真的不知道程序的长度是多少行(是的,这是一个指标)
  3. 相当多的函数式语言仍然比广泛使用的命令式静态语言做得更好——并不是函数式编程语言不简洁,而是动态语言允许更简洁的程序
  4. 至少在 Haskell 中,简洁的潜力来自于你可以自己构建的抽象——但你必须自己构建它们并将它们包含在你的解决方案中。一个聪明的 Haskell 黑客可能会在 20 行中实现一个 monad,它允许在 20 行而不是 30 行中解决一个小问题——这种抽象不会为一个小程序带来回报,但可以在一个更大的程序中节省很多行(例如 200 行而不是300) 程序。我想这同样适用于 Lisps(只有宏而不是 monad)
  5. 不要把粉丝看得太重。FP 很棒,值得研究,但它不能治愈癌症,也不能神奇地将任何代码缩短 25%
  6. 在某些领域,它们仍然可以击败动态语言:例如,由于代数数据类型和模式匹配,树状数据结构及其处理在许多函数式语言中非常自然地表达。
于 2010-08-26T22:08:36.020 回答
9

这似乎是一个鞭策的机会:

是否有统计研究表明 Python “更有生产力”?

关键是,最初的问题是试图使用一些(微不足道的,不适当的)数据来概括编程语言之间的比较。但事实上,几乎不可能使用任何数据对编程语言进行任何合理的一般定量比较。

不过,这里有一些值得深思的地方:

  • 在所有条件相同的情况下,动态类型语言可能更简洁,因为它们不需要花时间描述数据类型
  • 在所有条件相同的情况下,在静态类型语言中,类型推断语言对我来说可能更简洁,因为它们不需要到处声明类型
  • 在所有条件相同的情况下,在静态类型语言中,具有泛型/模板的语言更简洁,因为没有它们的语言需要重复代码或强制转换和间接
  • 在所有条件相同的情况下,具有简洁 lambda 语法的语言可能更简洁,因为 lambda 可能是编程中避免重复和样板代码最重要的抽象

也就是说,所有事物都不是平等的,并非遥不可及。

于 2010-08-28T06:09:04.623 回答
2

我最近将一个相对较短的 Java 程序移植到 OCaml。我过去曾涉足过 SML 和 Haskell,以及 C 方面的丰富经验。

在这个答案中,我将命令式代码与函数式代码(即没有突变)进行比较。如果您允许命令式代码潜入其他功能程序,您在比较什么?

根据我的经验,纯函数式编程 (PFP​​) 很优雅,但并不比命令式更简洁。PFP 中的“声明”样板较少,但“转换”样板较多;例如,从元组中解包、尾递归辅助函数等。因此,这两种范式都不允许程序的纯“肉”不受阻碍地表达。

PFP 具有较低的运行成本,并且编写程序来证明给定算法在原则上有效,在 PFP 中运行良好。但是,将其扩展为“真实世界”,让它处理错误条件和非法输入以及打印诊断会增加很多在命令式语言中更容易克服的臃肿。

于 2015-03-10T09:09:12.797 回答
2

Alioth 游戏中的程序通常不能真正代表这些语言的程序。一方面,那里的实现针对 Shootout 的特定基础架构进行了高度优化,这可能导致函数式语言中的惯用语更少,代码更臃肿。这类似于一些 Ruby 库将如何用 C 编写性能关键代码——查看 C 代码并声明 Ruby 臃肿且低级实际上不会给该语言带来公平的震动。

另一方面,为什么函数式语言被吹捧为如此简洁的很大一部分原因是它们擅长进行抽象。这往往在大型程序中比在单一功能奇迹中更有帮助,因此专门为简单而设计的语言在那里大获全胜。Python、Perl 和 Ruby 是专门为缩短程序而设计的,而大多数函数式语言还有其他目标(尽管这并不是说它们也只是忽略代码大小)。

于 2010-08-28T05:35:55.257 回答
1

您应该考虑这样一个事实,即您的第 1 组语言(脚本)比 C/C++ 慢 30 到 100 倍,对于函数式语言,同样是 2 到 7 倍。列表中的程序针对速度进行了优化,测量其他任何内容都是次要问题,这并不是语言真实状态的良好指标。查看代码大小和运行时间的权重均为 1 的表格
会更有趣。这样您就可以比较速度/可维护性比率,这似乎比代码大小更好。

于 2010-08-27T10:55:49.027 回答
1

必须与可用于您级别 1 中的大多数语言的扩展 OOP 库有关,并且只是简单的旧 hack,例如用于 shell 调用的反引号和 perl 正则表达式语法。离开蟒蛇

pw = file("/etc/passwd")
for l in pw:
    print l.split(':')[0]

如果不是因为 OO 语言充斥着抽象,打印系统上的所有用户名将需要更多的代码。我并不是说它不能在其他范式中完成,但趋势是每种类型都有很多成员函数,可以让繁琐的任务变得简单。就我个人而言,我发现纯函数式语言仅对学术目的有用(但我又知道什么)。

于 2010-08-26T21:54:21.127 回答
-1

获胜者似乎是普通的老式动态语言。

Lisp 是一个明显的反例,它是一种非常冗长的普通旧动态语言。另一方面,APL/J/K 可能比任何其他语言都简洁得多,而且它们是动态的。还有数学...

Haskell 和 Common Lisp 看起来并不比自称冗长的 Java 更简洁。

您的数据适用于针对性能进行了优化的微型程序,衡量标准是在特定设置下使用 GZIP 算法压缩后的代码大小因此,您不可能仅从它们中得出一般性结论。也许更有效的结论是您正在观察性能优化导致的膨胀,因此您的数据中最简洁的语言是那些由于根本上效率低下而无法优化的语言(Python、Ruby、Javascript、Perl、Lua、PHP)。相反,Haskell 可以通过足够的努力进行优化,以创建快速但冗长的程序。这真的是 Haskell 与 Python 的劣势吗?另一个同样有效的结论是 Python、Ruby、Perl、Lua 和 PHP 在该设置上使用 GZIP 算法可以更好地压缩。也许如果您使用游程编码或算术编码或 LZ77/8 重复实验,也许使用 BWT 预处理或其他算法,您会得到完全不同的结果?

该网站上的代码中还有大量毫无价值的杂物。查看这段 OCaml 代码片段,仅当您的 OCaml 安装已过时两代时才需要:

(* This module is a workaround for a bug in the Str library from the Ocaml
 * distribution used in the Computer Language Benchmarks Game. It can be removed
 * altogether when using OCaml 3.11 *)
module Str =
struct
  include Str

  let substitute_first expr repl_fun text =
    try
      let pos = Str.search_forward expr text 0 in
      String.concat "" [Str.string_before text pos;
                        repl_fun text;
                        Str.string_after text (Str.match_end())]
    with Not_found ->
      text

  let opt_search_forward re s pos =
    try Some(Str.search_forward re s pos) with Not_found -> None

  let global_substitute expr repl_fun text =
    let rec replace accu start last_was_empty =
      let startpos = if last_was_empty then start + 1 else start in
      if startpos > String.length text then
        Str.string_after text start :: accu
      else
        match opt_search_forward expr text startpos with
        | None ->
            Str.string_after text start :: accu
        | Some pos ->
            let end_pos = Str.match_end() in
            let repl_text = repl_fun text in
            replace (repl_text :: String.sub text start (pos-start) :: accu)
                    end_pos (end_pos = pos)
    in
      String.concat "" (List.rev (replace [] 0 false))

  let global_replace expr repl text =
    global_substitute expr (Str.replace_matched repl) text
  and replace_first expr repl text =
    substitute_first expr (Str.replace_matched repl) text
end

单核版本通常包含大量并行代码,例如OCaml 中的 regex-dna。看看OCaml 中 fasta的怪物:整个程序被复制了两次,并且它打开了字大小!我在磁盘上有一个旧的 OCaml 版本的 fasta,它的大小不到那个的五分之一......

最后,我应该指出,我向这个站点贡献了代码,但它被拒绝了,因为它太好了。抛开政治不谈,OCaml 二叉树曾经包含“由 Isaac Gouy 优化”的语句(尽管注释已被删除,但优化仍然存在,使得 OCaml 代码更长更慢),因此您可以假设所有结果已被主观篡改,以引入偏见。

基本上,有了如此低质量的数据,您就无法得出任何有见地的结论。您最好尝试找到已在语言之间移植的更重要的程序,但即便如此,您的结果也将是特定领域的。我建议完全忘记枪战......

于 2010-09-05T12:38:45.427 回答