繁体   English   中英

Scala程序员 - “应该有一种明显的方法来实现它”或“不止一种方式”吗?

[英]Scala programmer - “should there be one obvious way to do it” or “more than one way to do it”?

我已经做了很长一段时间的Java并且大约6个月前开始使用Scala。 我喜欢这门语言。 我发现的一件事是有多种方法可以做。 我不知道这是因为语言的性质还是因为它还很年轻而且不断发展,习惯用法和最佳实践还没有出现。

让我感到惊讶的是,我一直是perl人的蟒蛇,其中:

“应该有一个 - 最好只有一个 - 明显的做法。”

对我来说比对我更有意义

“有不止一种方法可以做到这一点”

我很想知道你认为Scala适合这种规模的原因以及为什么?


行情: Python PEP20Perl引用

我对Scala的经验是,有不止一种方法可以做事,但是做事情的方法相对较少。 图书馆以不同方式做事的几乎没有任何选择; 当它这样做时,通常是在一部分案例中实现额外的表现力或紧凑性或效率。

例如,如果你想对一个整数数组求和,那么你可能完全是程序性的,这会产生尽可能快的代码,但代价是有点笨重 - 但如果这是真正的时间关键代码,这是解决问题的唯一最佳方法:

val array = Array(1,2,3,4,5)
var sum = 0
var index = 0
while (index < array.length) {
  sum += array(index)
  index += 1
}
sum

使用基元时,同样通用的功能方法速度较慢(这可能会随着2.8中的@specialized而改变),但会让你减少乏味:

var sum = 0
Array(1,2,3,4,5).foreach(x => sum += x)
sum

然后有一些稍微不那么通用的功能结构就是为这类问题设计的(因为它出现了很多),如果你想要干净,紧凑的代码,这是“最好的”:

Array(1,2,3,4,5).reduceLeft( _ + _ )

有时会有非常非常规的结构来完全按照你的意愿行事; 例如,在Scala 2.8中:

Array(1,2,3,4,5).sum

因此,您可以在一般功率,紧凑性,清晰度和速度方面进行权衡,从而获得连续的选择。 如果你可以假设只需要熟悉Scala的人可以访问代码,并且你知道你是否需要绝对最佳的性能,那么好的选择通常是相当有限的。 但是,由于语言和库的表达能力,通常有许多可能的次要方法来做事。

Scala是一种包含许多不同哲学和设计方法多范式语言 这些包括

  • 面向对象
  • 功能编程
  • 特定于Java的方法
  • 面向语言的编程(扩展语法,元编程)

这允许程序员从许多不同的角度解决问题,因此在许多方面。 正如您所怀疑的那样,这就是语言的本质(就像其他通用/多范式语言一样)。

使用哪一个很大程度上取决于程序员,虽然我可以给出一些一般规则/想法:

  • 毕竟,Scala是一种功能语言 因此,我认为遵循一种功能性的编程风格是一种很好的做法(这也将导致非常简洁和优雅的解决方案)

  • 始终如一! 毕竟,应该有一种方式 ,但你可以选择哪一种方式 避免混合副作用和纯函数,列表推导和库函数(映射,过滤)或Java-与功能思维混合。 相反,在他们最佳服务的地方正交使用不同的范例。

  • 使用Scala为您提供的精彩功能 (而不仅仅是将它用作具有良好语法的Java):模式匹配,延续,泛型和极其强大的类型系统,高阶函数。

Scala绝对是一种超越单行的语言。 特别是,因为它是一种高度正交的语言,尽可能少的例外,并尽可能通过库而不是语言本身提供。

正交意味着语言特征不是相互依赖的。 相反,它们可以自由组合以实现任何目的。 这使语言更具表现力 - 并使语言更简单 - 但它肯定不会“引导”程序员,通过强制优先的方式,或通过禁止可疑价值的组合。

几乎没有例外是高正交性的结果。 Scala中的事情并没有被禁止,因为它们没有意义(从设计者的角度来看)或者不是最理想的。

最后,强调图书馆。 Scala没有列表或地图。 并且仅指定数组,因为与Java的互操作性要求它这样做。 从这个意义上说,它与C语言非常相似,而不是BASIC。 在C中,除了数学之外你几乎所做的一切都是通过库完成的,用C本身或汇编语言编写。 在BASIC中,即使打开文件或在屏幕上显示文本等命令也是语言本身的一部分。

这有两个实际后果。 首先,有一种趋势是拥有众多的职责重叠的图书馆。 当然,对于任何具有强大图书馆生态系统的语言来说都是如此,但在Scala中,这些库看起来就像语言本身的一部分;

另一个实际结果是基础库需要一定的灵活性,这会在它们之间产生一些冗余。

例如, List有一个追加运算符(在Scala 2.8上),尽管这对于列表来说是次优的。 它有它因为ListSeq ,这是一个人们可能想要做的一般操作。

最后要注意的是,在功能和面向对象编程的结合中可以看到该语言的多方位特性。

最明显的地方,Scala提供实现同样的事情完全不同的方式在区域for内涵:

val ls = List(1, 2, 3, 4)
for (l <- ls) println(l) //for comprehension
ls.foreach(println(_))   //underlying form 

这扩展到flatMapfilter等。我个人的选择是坚持使用方法调用,尽管在更复杂的例子中如下:

for (i <- 1 to 10;
     j <- 1 to 20 if (j % 2 == 0)) ...

我可以看到这些都是简单,更直观的for形式。 对于它的价值,我重视选择你的风格的能力,因为来自不同背景的人喜欢不同的东西。

将控制结构添加到2.8就像Exceptions就是一个例子:来自Java背景我起初没有得到这些抽象背后的力量,但如果我从功能土地开始那么我可能会直接理解远。

我在Scala中的主要缺点是对选项类缺乏合理的谓词理解。 即使对于某些谓词p

opt.isEmptyOr( p ) //does not exist
opt.forall( p )

是等价的,我不喜欢前者从可读性角度来看不存在的事实。 同样:

opt.notEmptyAnd( p ) //does not exist       
opt.map(p).getOrElse(false)

在我看来,第二种形式是不必要的不​​可读! 重复实现可读性肯定是积极的!

整个概念是如此模糊,以至于毫无意义。 但我怀疑,如果你正在使用一种语言,其中只有一种方法可以做任何给定的事情,那么你至少有50%的时间都在哭泣并咬牙切齿。

我(模糊)的感觉是,在任何体面的通用编程语言中, 总会有很多方法来完成大多数事情。

免责声明:我不知道Scala的作者对此有何看法,我只是表达了我对scala(以及python和ruby)开发人员的看法。

通过Scala API,您没有看到很多方法来做同样的事情。 例如,对象的Mutable和Immutable版本具有相同的接口。 这样的事情让Scala有了一些连贯性。

作为一种混合语言,通常有两种解决问题的方法:功能和程序方式(foreach vs for)。 当然,如果您想使用过程方式,那么您将只使用Java。

如果你看一下像Lisp和Haskell这样的函数式语言,通常有一种方法可以为你采用的每种方法做到这一点(例如,因为你可以解决递归或列表推导中的问题)。

最后,我相信任何代码都应该是显而易见的。 写得很明显。 你可以非常灵活(perl / ruby​​)或严格(python)。 Scala很灵活(想想在方法调用中使用“。”和括号的语法,但是你可以按照你想要的那样严格。个人而言,我喜欢严格的事情,所以我会编写自己的代码,我认为这是显而易见的方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM