Scala编码风格和约定?

时间:2010-09-15 14:36:40

标签: scala

我认为Scala与简单性相差太远,就像它的语法一样。例如Martin Odersky在他的书中写了这个方法:

def calculate(s: String): Int =
  if (cache.contains(s))
    cache(s)
  else {
    val acc = new ChecksumAccumulator
    for (c <- s)
      acc.add(c.toByte)
    val cs = acc.checksum()
    cache += (s -> cs)
    cs
  }

如果方法增长,读取代码变得非常痛苦,我无法匹配花括号,无法在IDE中折叠方法。 那里有Scala编码约定吗?我觉得表达一个简单的方法太灵活了:

def add(b: Byte): Unit = {
  sum += b
}

def add(b: Byte): Unit = sum += b

def add(b: Byte) { sum += b }

7 个答案:

答案 0 :(得分:35)

“如果方法增长,那么阅读代码会变得非常痛苦”。我认为部分答案是方法不应该增长。功能编程风格有很多小方法。计算方法已经偏大。

回答有关Scala编程风格指南的更一般问题:这是一个representative example

答案 1 :(得分:27)

以下是Scala Style Guide的链接。


The Curly Braces部分说:

  

<强>大括号:

     

应省略大括号   控制结构的情况   代表一种纯粹的功能   经营和所有分支机构   控制结构(与...相关)   if / else)是单行表达式。   请记住以下准则:

     
      
  • if - 如果你有一个else子句,则省略括号。否则,环绕   内容与花括号即使   内容只有一行。

  •   
  • while - 从不省略大括号(虽然不能用于纯功能   方式)。

  •   
  • for - 如果你有一个yield子句,则省略括号。否则,环绕   带有花括号的内容,即使   内容只有一行。

  •   
  • case - 如果案例表达式ts在一行上,则省略括号。   否则,请使用花括号   清晰度(即使它们不是   解析器所需的。)

    val news = if (foo)
      goodNews()
    else
      badNews()
    
    if (foo) {
      println("foo was true")
    }
    
    news match {
      case "good" => println("Good news!")
      case "bad" => println("Bad news!")
    }
    
  •   

我希望人们遵循这个风格指南:(


请注意,如果“ifelse条款”部分,我不同意“Omit braces”。我更喜欢看到这样的代码:

def calculate(s: String): Int = {
  if (cache.contains(s)) {
    cache(s)
  } else {
    val acc = new ChecksumAccumulator
    for (c <- s) {
      acc.add(c.toByte)
    }
    val cs = acc.checksum()
    cache += (s -> cs)
    cs
  }
}

答案 2 :(得分:7)

答案 3 :(得分:4)

您引用的特定示例可能看起来很复杂,因为它使用缓存来记忆结果。如果删除了memoization,则方法简化为:

def calculate(s: String): Int = {
    val acc = new ChecksumAccumulator
    for (c <- s)
        acc.add(c.toByte)
    acc.checksum()
}
我认为,这根本不复杂。

答案 4 :(得分:2)

以下是写scala的人,他自己,仍然是这种语言的主要实现者。

来源:Scala中的函数式编程原理 作者:Martin Odersky

您也被邀请参加他的课程或下一次提供link [1]

===========================

Scala风格指南帮助

在此页面上,您可以找到我们在查看某些提交内容时检测到的常见问题列表。

自动样式检查器可以检测到一些样式问题,我们也将其用于评分过程。样式检查器基于Scalastyle,可以通过运行styleCheck任务在sbt中本地执行。

常见问题

1避免强制转换和类型测试

永远不要使用isInstanceOf或asInstanceOf - 总是有更好的解决方案,既可用于分配,也可用于任何真实的Scala项目。如果你发现自己想要使用演员阵容,那就退后一步思考你想要实现的目标。重新阅读作业说明,再看一下相应的讲座视频。

2缩进

确保您的代码正确缩进,它变得更具可读性。

这可能看起来微不足道,与我们的练习不太相关,但想象自己将来是团队的一员,与其他程序员一起处理相同的文件:每个人都尊重样式规则来保持代码是非常重要的。健康。

如果您的编辑器没有按照您希望的方式进行缩进,您应该了解如何更改其设置。在Scala中,标准是使用2个空格缩进(无标签)。

3行长度和空白

确保线条不会太长,否则您的代码很难阅读。不要写很长的行,而是引入一些本地值绑定。统一使用空格可以使代码更具可读性。

示例(长行,缺少空格):

if(p(this.head))this.tail.filter0(p, accu.incl(this.head))else this.tail.filter0(p, accu)

更好:

if (p(this.head))
  this.tail.filter0(p, accu.incl(this.head))
else
  this.tail.filter0(p, accu)

更好(见下面的#4和#6):

val newAccu =

 if (p(this.head)) accu.incl(this.head)
  else accu
this.tail.filter0(p, newAccu)

4使用本地值来简化复杂的表达式

在以函数样式编写代码时,方法通常作为函数调用的组合实现。如果这样的组合表达式变得太大,代码可能会变得难以理解。

在这种情况下,最好将一些参数存储在本地值中,然后再将它们传递给函数(参见上面的#3)。确保本地值具有有意义的名称(请参阅下面的#5)!

5为方法和值选择有意义的名称

应仔细选择方法,字段和值的名称,以便易于理解源代码。方法名称应该清楚该方法的作用。不,temp永远不是一个好名字: - )

一些可以改进的例子:

val temp = sortFuntion0(list.head, tweet)   // what does sortFunction0 do?
def temp(first: TweetSet, second : TweetSet): TweetSet = ...
def un(th: TweetSet,acc: TweetSet): TweetSet = ...
val c = if (p(elem)) accu.incl(elem) else accu
def loop(accu: Trending, current: TweetSet): Trending = ...
def help(ch: Char, L2: List[Char], compteur: Int): (Char, Int) = ...
def help2(L: List[(Char, Int)], L2: List[Char]): List[(Char, Int)] = ...

6个常见的Subexpressions

您应该避免对计算密集型方法进行不必要的调用。例如

this.remove(this.findMin).ascending(t + this.findMin)

调用this.findMin方法两次。如果每次调用都很昂贵(例如必须遍历整个数据结构)并且没有副作用,则可以通过引入本地值绑定来保存一个:

val min = this.findMin
this.remove(min).ascending(t + min)

如果以递归方式调用函数,这变得更加重要:在这种情况下,该方法不仅会被调用多次,而且会被指数次。

7不要复制粘贴代码!

复制粘贴代码始终是糟糕风格的警告标志!有许多缺点:

代码更长,理解它需要更多时间 如果两个部分不相同但非常相似,则很难发现差异(参见下面的示例) 维护两个副本并确保它们保持同步非常容易出错 更改代码所需的工作量成倍增加 您应该将公共部分分解为单独的方法,而不是复制代码。示例(另见上面的#3):

val googleTweets: TweetSet = TweetReader.allTweets.filter(tweet =>
  google.exists(word => tweet.text.contains(word)))
val appleTweets: TweetSet = TweetReader.allTweets.filter(tweet =>
  apple.exists(word => tweet.text.contains(word)))
This code is better written as follows:

def tweetsMentioning(dictionary: List[String]): TweetSet =
  TweetReader.allTweets.filter(tweet =>
    dictionary.exists(word => tweet.text.contains(word)))

val googleTweets = tweetsMentioning(google)
val appleTweets  = tweetsMentioning(apple)

8 Scala不需要分号

只有在同一行上写多个语句时才需要Scala中的分号。应避免编写不必要的分号,例如:

def filter(p: Tweet => Boolean): TweetSet = filter0(p, new Empty);

9请勿使用“打印”语句提交代码

在提交代码之前,您应该清理代码并删除所有print或println语句。一旦您为公司工作并创建在生产中使用的代码,这同样适用:最终代码应该没有调试语句。

10避免使用Return

在Scala中,您通常不需要使用显式返回,因为控件结构(如if)是表达式。例如,在

def factorial(n: Int): Int = {
  if (n <= 0) return 1
  else return (n * factorial(n-1))
}

可以简单地删除return语句。

11避免使用可变的局部变量

由于这是关于函数式编程的课程,我们希望您习惯于以纯函数式编写代码,而不使用副作用操作。您通常可以重写使用可变局部变量的代码来编写带有累加器的辅助函数。而不是:

def fib(n: Int): Int = {
  var a = 0
  var b = 1
  var i = 0
  while (i < n) {
    val prev_a = a
    a = b
    b = prev_a + b
    i = i + 1
  }
  a
}

喜欢:

def fib(n: Int): Int = {
  def fibIter(i: Int, a: Int, b: Int): Int =
    if (i == n) a else fibIter(i+1, b, a+b)
  fibIter(0, 0, 1)
}

12消除冗余的“If”表达式

而不是

if (cond) true else false

你可以简单地写

cond

(同样对于否定案例)。

其他造型问题? 请使用样式或styleChecktags发布到论坛,我们将根据建议添加此样式指南。

答案 5 :(得分:0)

此网站http://docs.scala-lang.org/style/

中有很多约定

答案 6 :(得分:0)

是的,这有点晚了,但是我想发布它,因为它反映了一种真实的方法,一种“全局可读性和可维护性”,并且包括来自Apache Spark贡献者的输入:Databricks Scala Style Guide < / p>