【问题标题】:If functional languages are really concise, why don't they have a better rank in the language shootout game?如果函数式语言真的很简洁,为什么它们在语言枪战游戏中没有更好的排名呢?
【发布时间】:2011-04-04 12:46:29
【问题描述】:

我在语言枪战游戏by their code size only 中比较了语言。这是我得到的摘要(最短的优先,按相似分数分组)。

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

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

更新:

我找到了一个关于这个主题的insightful post 图表。我还找到了similar comparison of languages for a larger program(一个简单的光线追踪器)。总而言之,我不会说我得到了“答案”,但我得到了一些思考。

【问题讨论】:

  • 您能否进一步解释一下枪战游戏在做什么?您链接的网站令人费解。
  • 是什么让您认为每个示例都是由该特定语言的专家编写的?查看 F# 代码,我看到从 C# 到 F# 的直接移植,它本身是从 Java 移植的。几乎没有惯用的功能代码。由于代码似乎主要由对 C++ 库的 P/Invoke 调用组成,我什至不确定这个基准测试应该测量什么——我猜是 Mono 的 P/Invoke 速度,因为基准测试显然甚至没有运行在 MS 运行时...
  • 我认为 haskell 做得很差,因为一​​旦你开始优化它以提高速度,haskell 代码就会膨胀很多。
  • 我不知道为什么有人会关心代码的长度?这并不是我衡量编程语言有多好的指标。
  • @igouy - 这个问题从讨论函数式语言的简洁性开始。请告诉我衡量 F# 与 GMP 库互操作性的基准如何告诉我 F# 语言的简洁性。我的观点只是,我为我熟悉的函数式语言找到的第一个源代码本质上并不是特别实用 - 这让人质疑使用这个特定基准作为衡量函数式语言的简洁程度的想法。跨度>

标签: programming-languages functional-programming comparison code-size


【解决方案1】:

必须与可用于您级别 1 中的大多数语言的扩展 OOP 库以及简单的旧技巧有关,例如用于 shell 调用的反引号和 perl 正则表达式语法。离开python

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

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

【讨论】:

  • 这可以解释为什么动态语言得分更高。但这并没有回答为什么函数式语言得分如此之低的问题。
  • 这并不能解释任何事情。如果函数式语言有丰富的库,我们可以写类似file("/etc/passwd") map l.split(':')[0]
  • 功能语言面向学术界,它们没有广泛的图书馆社区,这意味着您必须自己做更多的事情。而且,由于面向学术界,函数式语言无论如何解决了完全不同的问题(并且比动态语言更简洁)。
  • 在 haskell 中它是 readFile "/etc/passwd" >>= mapM (putStrLn . takeWhile (/=':')) . lines,它长 7 个字符(如果在 python 和 haskell 示例中最小化空格,则为 3)。其中四个是因为它在 haskell 中称为 readFile 在 python 中称为 file
  • 您描述的任务是一个系统脚本任务,主要是为 Python 等语言设计的。所以它擅长它也就不足为奇了。函数式语言在其他任务上会做得更好(例如处理具有复杂结构的数据)。
【解决方案2】:
  1. 没有一种语言总是优于另一种语言(嗯,也有一些例外...... ;)),所以同样适用于一组广泛分类的语言。基准涵盖了广泛的主题,X 可能不如 Y 适合其中一个。
  2. 源代码被 gzip 压缩,我们真的不知道程序有多少行长度(是的,这是一个指标)
  3. 相当多的函数式语言仍然比广泛使用的命令式静态语言做得更好 - 并不是函数式编程语言不简洁,而是动态语言允许更简洁的程序
  4. 至少在 Haskell 中,简洁性的巨大潜力来自您可以自己构建的抽象 - 但您必须自己构建它们并将它们包含在您的解决方案中。一个聪明的 Haskell 黑客可能会在 20 行中实现一个 monad,它允许在 20 行而不是 30 行中解决一个小问题——这种抽象不会为一个小程序带来回报,但可以在一个更大的程序中节省很多行(例如 200 行而不是300) 程序。我想这同样适用于 Lisps(只有宏而不是 monad)
  5. 不要太认真地对待粉丝。 FP 很棒,值得研究,但它不能治愈癌症,也不能神奇地将任何代码缩短 25%
  6. 在某些领域它们仍然可以胜过动态语言:例如,由于代数数据类型和模式匹配,树状数据结构及其处理在许多函数式语言中非常自然地表达。

【讨论】:

  • >> 程序的长度在哪里(是的,它是一个指标)
  • @djv - 这就是源代码被压缩的原因 - 但 delnan 似乎认为 LoC 会更好。
  • 我认为 LoC 字符数可能更多的指标会更好。
  • 是的,没有人提到 J. 用于科学计算,代码大小几乎无与伦比!
  • 压缩代码将大幅抑制重复,例如冗长的关键字繁重的习语。压缩代码绝不是源大小的远程准确指标。
【解决方案3】:

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

1 - Programming in the large is different than programming in the small

不应以这些小型基准游戏程序为例,说明每种语言提供的抽象和模块化如何应用于大型编程。

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

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

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

3 - 没有删除流星竞赛项目,流星竞赛是没有限制的编程竞赛 - the smallest meteor-contest Haskell program is the slowest meteor-contest Haskell program

【讨论】:

  • +1 因为我不能 +10。枪战不会在任何有用的意义上测试简洁。 10 行 vs 20 并不重要,重要的是 10000 vs 20000 或 10000000 vs 20000000。哦,了解这 10M 行。
  • 1 - 该维基百科链接无法确认您的声明。如果一种语言有很好的抽象方法,它应该有更好的 API,更容易调用。 2 - 我在网站上找不到任何关于他们偏向于更快程序的参考。
  • @Adam Schmideg 1 维基百科解释了小程序和大程序之间的基本区别。这些小程序不提供创建或使用大程序提供的抽象的机会。 2 没有偏见,一切都是为了更快的程序!帮助页面“我在哪里可以看到以前的程序?” shootout.alioth.debian.org/help.php#previous
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    【解决方案6】:

    这似乎是一个抽身的机会:

    Are there statistical studies that indicates that Python is "more productive"?

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

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

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

    也就是说,所有事物都不是平等的,远非如此。

    【讨论】:

      【解决方案7】:

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

      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 代码的 sn-p 代码,仅当您的 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
      

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

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

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

      【讨论】:

      • >> 使用 OCaml 3.11 时可以完全删除它
      • >> 只是因为
      • @iguoy:“可惜没有 OCaml 党派贡献了最新的代码”。您需要一个 OCaml 支持者为您从您的网站中删除该代码块吗?
      • 当 OCaml 专家注意到他们可以对 OCaml 程序进行简单改进时,您会猜测他们会继续提供经过测试的程序来进行更改,还是会在公共论坛上花费 5 分钟抱怨代码?去图吧。
      • 我认为最好让那些认为自己是 OCaml 程序员的人贡献 OCaml 程序,而不是那些声称自己是 OCaml 程序员的人。
      【解决方案8】:

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

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

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

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

      【讨论】:

      • 确实如此。这就是为什么大多数人更喜欢不纯的函数式语言。图算法是一个很好的案例研究,其中纯解决方案与命令式解决方案相比往往看起来和执行起来非常糟糕。
      猜你喜欢
      • 2014-11-24
      • 2020-12-07
      • 1970-01-01
      • 2013-04-05
      • 1970-01-01
      • 2010-09-28
      • 2010-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多