Lee*_*ite 13 functional-programming if-statement coding-style conventions
我记得有些时候(可能是几年前)我在Stackoverflow上阅读了尽可能少的if-tests编程的魅力.这个问题有点相关,但我认为压力在于使用许多小函数,它们根据接收到的参数返回由测试确定的值.一个非常简单的例子就是使用它:
int i = 5;
bool iIsSmall = isSmall(i);
Run Code Online (Sandbox Code Playgroud)
用isSmall()看起来像这样:
private bool isSmall(int number)
{
return (i < 10);
}
Run Code Online (Sandbox Code Playgroud)
而不只是这样做:
int i = 5;
bool isSmall;
if (i < 10) {
isSmall = true;
} else {
isSmall = false;
}
Run Code Online (Sandbox Code Playgroud)
(逻辑上这段代码只是示例代码.它不是我正在制作的程序的一部分.)
我相信这样做的原因是因为它看起来更好并且使程序员不易出现逻辑错误.如果正确应用了这种编码约定,你几乎看不到任何地方的if-tests,除了那些唯一目的是进行测试的函数.
现在,我的问题是:有关于此约定的任何文档吗?在这种风格的支持者和反对者之间是否有任何地方可以看到疯狂的争论?我试着搜索引入我的Stackoverflow帖子,但我再也找不到了.
最后,我希望这个问题不会被打倒,因为我不是要求解决问题.我只是希望能够听到更多关于这种编码风格的信息,并且可能会提高我将来会做的所有编码的质量.
这整个"if"vs"no if"的事情让我想到了表达式问题1.基本上,它使用if语句或没有if语句编程的观察是封装和可扩展性的问题,有时使用if语句2更好,有时更好地使用方法/函数指针进行动态调度.
当我们想要建模时,需要担心两个轴:
实现此类事情的一种方法是使用if语句/模式匹配/访问者模式:
data List = Nil | Cons Int List
length xs = case xs of
Nil -> 0
Cons a as -> 1 + length x
concat xs ys = case ii of
Nil -> jj
Cons a as -> Cons a (concat as ys)
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用面向对象:
data List = {
length :: Int
concat :: (List -> List)
}
nil = List {
length = 0,
concat = (\ys -> ys)
}
cons x xs = List {
length = 1 + length xs,
concat = (\ys -> cons x (concat xs ys))
}
Run Code Online (Sandbox Code Playgroud)
不难看出使用if语句的第一个版本可以很容易地在我们的数据类型上添加新操作:只需创建一个新函数并在其中进行案例分析.另一方面,这使得很难将新案例添加到我们的数据类型中,因为这意味着返回程序并修改所有分支语句.
第二个版本恰恰相反.将新案例添加到数据类型非常容易:只需创建一个新的"类",并告诉我们要为每个需要实现的方法做些什么.但是,现在很难向界面添加新操作,因为这意味着为实现该接口的所有旧类添加新方法.
语言使用许多不同的方法来尝试解决表达式问题,并且可以轻松地将新案例和新操作添加到模型中.但是,这些解决方案有利有弊3所以一般来说我认为在OO和if语句之间进行选择是一个很好的经验法则,这取决于你希望用什么轴来扩展内容.
无论如何,回到你的问题,有几件事我想指出:
第一个是我认为删除所有if语句并用方法调度替换它们的OO"口头禅"更多地与大多数OO语言没有类型安全代数数据类型有关,它与"if"有关statemsnts"对封装不利.因为类型安全的唯一方法是使用方法调用,所以鼓励使用if语句将程序转换为使用访问者模式4的程序或更糟:使用简单方法调度将应该使用访问者模式的程序转换为程序,从而制作可扩展性容易在错误的方向.
第二件事是,我不是因为你可以把事情制成功能的忠实粉丝.特别是,我发现所有函数只有5行并调用大量其他函数的风格很难理解.
最后,我认为你的例子并没有真正摆脱if语句.从本质上讲,您正在做的是从Integers到新数据类型的函数(有两种情况,一种用于Big,一种用于Small),然后在使用数据类型时仍然需要使用if语句:
data Size = Big | Small
toSize :: Int -> Size
toSize n = if n < 10 then Small else Big
someOp :: Size -> String
someOp Small = "Wow, its small"
someOp Big = "Wow, its big"
Run Code Online (Sandbox Code Playgroud)
回到表达式问题的观点,定义我们的toSize/isSmall函数的优点是我们选择逻辑来选择我们的数字在一个地方适合的情况,并且我们的函数只能在之后的情况下操作.但是,这并不意味着我们已从代码中删除了if语句!如果我们将toSize作为工厂函数,并且我们有Big和Small是共享接口的类,那么是的,我们将从代码中删除if语句.但是,如果输出isSmall只返回一个布尔值或枚举,则会有与之前一样多的if语句.(并且您应该选择要使用的实现,具体取决于您是否希望更容易添加新方法或新案例 - 例如中等 - 将来)
1 - 问题的名称来自于具有"表达式"数据类型(数字,变量,子表达式的加法/乘法等)并且想要实现评估函数和其他内容之类的问题.
2 - 或代数数据类型的模式匹配,如果你想更加类型安全......
3 - 例如,您可能必须在"调度程序"可以看到的"顶级"上定义所有多方法.与一般情况相比,这是一个限制,因为您可以使用嵌套在其他代码中的if语句(和lambdas).
4 - 基本上是代数数据类型的"教会编码"