函数式程序的可读性


13

我对此感到很好奇,因为我在学习任何功能语言之前都回想起它们,我认为它们全都是可怕,可怕,难以理解的。既然我已经知道Haskell和f#,我发现花更少的时间来阅读更少的代码,但是那一点代码的功能远比命令语言中的等效功能要多得多,因此感觉像是纯收益,而且我并不是很极端在功能上练习。

这是我的问题,我不断地从OOP人士那里听到,功能样式非常难以理解。我很好奇是否是这种情况,并且我在自欺欺人,或者如果他们花时间学习一种功能语言,那么整个样式将不再比OOP更难以理解?

是否有人看到过任何证据或轶事,他们以一种或另一种方式频繁地说这句话?如果实际上编写功能的可读性较低,那么我不想继续使用它,但是我真的不知道是否是这种情况。


1
我记得在我对函数式编程几乎一无所知的时候,看过“超级纳里奥兄弟” svn.coderepos.org/share/lang/haskell/nario的代码,并记得在想“哇。这似乎真的读”
WuHoUnited



3
@BlackICE linq和lambdas 与定义“函数式编程” 相差很远。函数式编程更多地涉及类型系统,因为当您开始将函数识别为类型,而不仅仅是将对象/类/记录识别为类型时,您最终会获得许多新功能和新技术。LINQ和Lambda表达式(或列表monad和高阶函数)只是两种特定的合成技术,其中还有很多。
Jimmy Hoffa 2015年

Answers:


15

代码的可读性是非常主观的。因此,不同的人会认为同一代码可读或不可读。

作业x = x + 1对数学家来说听起来很奇怪,因为作业看起来与比较有误导性。

对于不熟悉这些模式的人来说,一个简单的OOP设计模式将是完全不清楚的。考虑单例。有人会问:“为什么需要私有构造函数?什么是神秘方法 getInstance() ?为什么我们不创建全局变量并在其中分配对象?”

FP代码也是如此。除非您了解幕后的逻辑模式,否则即使理解非常基础的内容也将非常困难,例如,如何将一个函数作为参数传递给另一个函数。

话虽如此,但我们应该理解,FP不是灵丹妙药。在许多应用中,FP难以应用,因此将导致可读性更差的代码


人们应该创造与自己(人类)相似的东西。例如,即使在计算机视觉中,我们现在也拥有神经网络。人体由对象,属性,方法组成,我们使用对象,属性,方法描述其他事物。所以函数式编程没有任何意义
user25

12

简短答案:

人们说功能性编程代码难以阅读的要素之一是,它偏爱更紧凑的语法。

长答案:

函数式程序设计本身不是可读或不可读的,因为它是一种范式,而不是一种代码编写方式。例如,在C#中,函数式编程如下所示:

return this.Data.Products
    .Where(c => c.IsEnabled)
    .GroupBy(c => c.Category)
    .Select(c => new PricesPerCategory(category: c.Key, minimum: c.Min(d => d.Price), maximum: c.Max(d => d.Price)));

并被任何具有Java,C#或类似语言的经验的人视为可读。

另一方面,与流行的OOP语言相比,对于许多功能语言(包括Haskell和F#)而言,语言语法更加紧凑,从而优先使用符号而不是普通英语单词。

这也适用于FP以外的语言。如果将流行的OOP语言与不那么流行的语言(通常使用更多的英语单词)进行比较,那么对于没有编程经验的人来说,最后一种会更容易理解。

比较:

public void IsWithinRanges<T>(T number, param Range<T>[] ranges) where T : INumeric
{
    foreach (var range in ranges)
    {
        if (number >= range.Left && number <= range.Right)
        {
            return true;
        }
    }

    return false;
}

至:

public function void IsWithinRanges
    with parameters T number, param array of (Range of T) ranges
    using generic type T
    given that T implements INumeric
{
    for each (var range in ranges)
    {
        if (number is from range.Left to range.Right)
        {
            return true;
        }
    }

    return false;
}

以相同的方式:

var a = ((b - c) in 1..n) ? d : 0;

可以用一种虚构的语言表示为:

define variable a being equal to d if (b minus c) is between 1 and n or 0 otherwise;

什么时候语法较短更好?

虽然对于初学者而言,更冗长的语法更容易理解,但是对于经验丰富的开发人员而言,更紧凑的语法更容易。较短的代码意味着更少的字符输入,这意味着更高的生产率。

尤其是,强迫人们键入关键字来指示可能从语法中推断出的内容确实没有任何意义。

例子:

  • 在PHP中,您需要function在每个函数或方法之前进行键入,而无需特殊原因。

  • Ada总是因为强迫开发人员键入很多英文单词而感到震惊,特别是因为没有正确的IDE自动完成功能。我最近没有使用过Ada,而且他们的官方教程已停刊,因此我无法举例。如果有人有例子,请随时修改我的答案。

  • 1..n许多FP(和Matlab)中使用的语法可以用between 1, nbetween 1 and n或代替between (1, n)。该between关键字使得它更容易理解的人谁不熟悉语言的语法,不过,两个点的速度会更快打字。


8
我同意这种观点,但是出于不同的原因。这与专家编写代码的难易程度无关,因为通常读取代码的频率远远高于编写代码的频率。如果较短的指示符已经很明显了,较短的代码不会浪费宝贵的时间,屏幕空间和思维能力,从而有助于提高可读性(例如,用换行符代替分号,无论如何这是两个巧合)。当它成为它也可以伤害神秘(比如,我喜欢for x in xsfor x xs,因为它有助于我区分循环变量和容器)。

我发现许多人都抱怨FP以及您刚才所说的C#代码片段,这也是FP的一个例子。如果大多数OOP人士认为该代码段可读性强,那么与其说是可读性,不如说是我的同事们的问题,尽管我并不完全相信大多数OOP人士从我的经验中都可以做到这一点。
Jimmy Hoffa 2012年

@Jimmy霍法:见programmers.stackexchange.com/a/158721/6605在那里我讨论完全C#代码片段,它是如何通过初级程序员察觉。
阿森尼·穆尔琴科

1
@Sergiy:在Java或C#中,方法签名中没有method关键字。范例:public int ComputePrice(Product product)。像PHP一样,添加这样的关键字只会增加混乱,而不是使内容更易于阅读。如果程序员不能从其签名中了解到函数是函数,那么该程序员可能没有任何经验,或者该代码比我见过的最糟糕的代码更不可读。
2014年

1
@Sergiy:在某些情况下,详细程度可以提供帮助(否则,我们会看到诸如的语言和代码p i ComputePrice(product): _.unitPrice * ~.cart[_]),但是某些语言将其推得太远了。functionPHP中冗余关键字的示例就是一个很好的例子。
2014年

6

我认为原因之一是函数式编程倾向于使用更多抽象的概念。对于熟悉和理解这些概念的人来说,这使代码更短,更易读(因为它们恰当地表达了问题的本质),但对于不熟悉它们的人来说,则是不可读的。

例如,对于功能强大的程序员来说,编写可能在Maybemonad或Eithermonad 中不同点失败的代码是很自然的。或者,在Statemonad 的帮助下编写有状态的计算。但是对于一个不了解单子概念的人来说,这一切似乎都像是某种黑色巫术。

因此,我认为即使在一组OOP人员中,也应该使用一些函数式编程,只要一个使用易于理解或学习的概念,例如使用功能或闭包在集合上进行映射。

(当然,这还取决于编写一些代码的程序员。您可以用任何一种语言编写可怕的,难以阅读的代码,也可以以一种简洁的风格编写代码。)


2

可读性与范式,模型等无关。越多的代码反映出您的问题领域的语义,则对此类问题领域的术语和惯用语的操作就越多,它的可读性就越高。

这就是为什么OOP代码根本不那么可读的原因:并不是很多现实世界中的问题都是按照层次分类法来表达的。对象之间相互传递消息是一个怪异的概念,它与任何“真实的”都相去甚远。令人惊讶的是,现实世界中有更多的概念可以自然地映射到功能成语。问题倾向于以声明方式定义,因此声明式解决方案更为自然。

尽管,除了纯功能,纯OOP,纯数据流或纯其他形式之外,还有许多其他语义可能更接近真实世界。由于这个事实,一种面向语言的解决问题的方法产生了最具可读性的代码,击败了所有现有的“纯”范例。使用特定领域的语言,每个问题都以其自己的自然术语表达。在促进DSL实施方面,功能语言比OOP主流技术稍好(尽管有更好的方法可供使用)。


1

代码的可读性是主观的。我敢肯定,有人会因为缺乏理解而批评它。在实现通用模式的方式上自然有不同的习惯用法。例如,在具有模式匹配的语言中,访客模式实际上没有任何意义

类型推断可能是一个棘手的功能。通常,它是发展的强大加速器,但是,由于缺少上下文导致混乱的错误,有时可能很难弄清所涉及的类型。这不仅限于功能编程语言-我也在C ++模板中看到了!

现代语言往往是多种范例。这包括C#和F#。可以用C#编写功能样式,也可以用F#编写命令式样式。批评功能性语言的人可能会用面向对象的语言编写功能性代码。

功能语言的商业开发还相对不成熟。我已经看到许多错误,在任何语言中都将被视为错误形式。如:

  1. 当您学习语言时(而不是重构)改变风格会导致不一致。
  2. 一个文件太多。
  3. 一行上的语句过多。

关于错误,我还要补充:4.过度使用符号,例如:〜@#$%^&*()-_ + = \ | /。;; 5.隐式功能:可选的声明/关键字/语句,隐式转换等;
Sergiy Sokolenko 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.