记录Scala功能链

Ral*_*lph 4 documentation functional-programming scala

Scala(以及函数式编程,通常)提倡一种编程风格,您可以在其中生成表单的功能"链"

collection.operation1(...).操作2(...)...

其中所述操作是的各种组合map,filter等等.

如果等效的Java代码可能需要50行,则Scala代码可以在1行或2行中完成.功能链可以将输入集合更改为完全不同的输入集合.

Scala代码的缺点是10分钟后(从不介意6个月后),我无法弄清楚我在想什么,因为符号是如此紧凑,并且缺少类型信息(因为隐含类型).

你怎么记录这个?您是否在链条之前放置了一个大块注释,将优雅的1线解决方案转换为由39行评论组成的庞大的40行解决方案?你是否像这样穿插你的评论?

collection.
  // Select the items that meet condition X
  filter(predicate_function).
  // Change these items from A's to B's
  map(transformation_function).
  // etc.
Run Code Online (Sandbox Code Playgroud)

别的什么?没有文件?(让他们猜测.他们永远不会"缩小"你,因为没有其他人可以维护代码.:-))

ham*_*mar 10

如果您发现自己在详细程度上撰写评论,那么您只需重复代码所说的内容即可.

对于长功能链,定义新功能以替换链的部分.给这些有意义的名字.然后你可以避免评论.这些函数的名称本身应该解释它们的作用.

最好的评论是那些解释为什么代码做了什么的评论.编写良好的代码应该使代码本身 "显而易见" .

  • +1:在Java中我认为大约4行是一个方法的好长度.在Scala中,有时候1行就是很多. (6认同)

Rex*_*err 8

我不会开始编写该代码(除非它是一次性使用的脚本或在REPL中播放).

如果我可以在一条评论中解释代码的作用并且读取正常,那么我将其保留为单行:

// Find all real-valued square roots and group them in integer bins
ds.filter(_ >= 0).map(math.sqrt).groupBy(_.toInt).map(_._2)
Run Code Online (Sandbox Code Playgroud)

如果我不能通过仔细阅读命令链来理解这一点,那么我应该将其分解为功能不同的单元.例如,如果我期望某人没有意识到负数的平方根不是实数,我会说:

// Only non-negative numbers have a real-valued square root
val nonneg = ds.filter(_ >= 0)
// Find square roots and group them in integer bins
nonneg.map(math.sqrt).groupBy(_.toInt).map(_._2)
Run Code Online (Sandbox Code Playgroud)

特别是,如果有人不熟悉Scala集合库,并且没有耐心花费5到10分钟来理解一行代码,那么他们不应该处理我的代码(也不是其他任何代码)这完成了一些他们不理解并且没有耐心去理解的非常重要的东西,或者我应该提前知道我除了编写工作代码之外还提供一个例如语言和数学教程,或者写一个段落解释以下行如何工作,或逐个命令地打破它,或在每个匿名函数的开头包括注释,解释正在发生的事情(视情况而定).

无论如何,如果你无法理解它的作用,你可能需要一些中间值.他们对精神重置非常有帮助("我看不出如何从A到C!......但是......好吧,我能理解A到B.我能理解B到C.")


Kev*_*ght 6

如果您链接操作都是一元的变换:map,flatMap,filter,那么它往往不多,更清晰的重写逻辑作为换理解.

coll.filter(predicate).map(transform)
Run Code Online (Sandbox Code Playgroud)

可能成为

for(elem <- coll if predicate) yield transform(elem)
Run Code Online (Sandbox Code Playgroud)

如果您有更长的操作序列,例如使用Kassen的示例,则更容易展示技术的力量:

def eligibleCustomers(products: Seq[Product]) = for {
  product <- products
  customer <- product.customers
  paying <- customer if customer.isPremium
  eligible <- paying if paying.age < 20
} yield eligible
Run Code Online (Sandbox Code Playgroud)