我已经做了很长时间的Java并且大约6个月前开始使用Scala。我喜欢这门语言。我发现的一件事是有多种方法可以做。我不知道这是因为语言的性质还是因为它还很年轻而且不断发展,习惯用法和最佳实践还没有出现。
让我感到惊讶的是,我一直是perl人的蟒蛇:
“应该有一个 - 最好只有一个 - 显而易见的方法。”
对我来说比
更有意义“有多种方法可以做到这一点”。
我很想知道您认为Scala适合这种规模的原因以及原因?
答案 0 :(得分:19)
我对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的人可以访问代码,并且你知道你是否需要绝对最佳的性能,那么好的选择通常是相当有限的。但是,由于语言和图书馆的表达能力,通常有许多可能的次要做法。
答案 1 :(得分:5)
Scala是multi-paradigm language包含许多不同的哲学和设计方法。其中包括
这允许程序员从许多不同的角度解决问题,因此在很多方面。正如您所怀疑的那样,这就是语言的本质(就像其他通用/多范式语言一样)。
使用哪一个很大程度上取决于程序员,虽然我可以给出一些一般规则/想法:
Scala毕竟是一种功能语言。因此,我认为遵循一种功能性的编程风格(这也将导致非常简洁和优雅的解决方案)是一种很好的做法。
保持一致! 毕竟,应该有一种方式,但您可以选择哪一种。避免混合副作用和纯函数,列表推导和库函数(映射,过滤)或Java-与功能思维混合。相反,在正确使用的地方使用不同的范例。
使用Scala为您提供的精彩功能(而不仅仅是将它用作具有良好语法的Java):模式匹配,延续,泛型和极其强大的类型系统,更高 - 订单功能。
答案 2 :(得分:4)
Scala绝对是一种超越单行的语言。特别是,因为它是一种高度正交的语言,尽可能少的例外,并尽可能通过库而不是语言本身提供。
正交意味着语言特征不是相互依赖的。相反,它们可以自由组合以实现任何目的。这使语言更具表现力 - 并使语言更简单 - 但它肯定不会“引导”程序员,无论是强迫一种偏好的方式,还是不允许组合可疑的价值。
很少有例外是高正交性的结果。斯卡拉的事情并没有被禁止,因为它们没有意义(从设计者的角度来看)或者不是最理想的。
最后,强调图书馆。 Scala没有列表或地图。并且仅指定数组,因为与Java的互操作性要求它这样做。从这个意义上说,它与C语言非常相似,而不是BASIC。在C中,除了数学之外你几乎所做的一切都是通过库完成的,用C本身或汇编语言编写。在BASIC中,即使打开文件或在屏幕上显示文本等命令也是语言本身的一部分。
这有两个实际后果。首先,有一种趋势是拥有众多的职责重叠的图书馆。当然,对于任何具有强大图书馆生态系统的语言来说都是如此,但在Scala中,这些库看起来就像是语言本身的一部分;
另一个实际结果是基础库需要一定的灵活性,这会在它们之间产生一些冗余。
例如,List
有一个追加运算符(在Scala 2.8上),即使这对列表来说不是最理想的。它有它,因为List
是Seq
,这是一个人们可能想要做的一般操作。
作为最后一点,在功能和面向对象编程的结合中可以看到该语言的多方位特性。
答案 3 :(得分:2)
scala以完全不同的方式实现相同目标的最明显的地方是for
理解:
val ls = List(1, 2, 3, 4)
for (l <- ls) println(l) //for comprehension
ls.foreach(println(_)) //underlying form
这扩展到flatMap
,filter
等。我个人的选择是坚持方法调用,尽管在更复杂的例子中如下:
for (i <- 1 to 10;
j <- 1 to 20 if (j % 2 == 0)) ...
我可以在for
表单中看到这些更简单,更直观。对于它的价值,我重视选择你的风格的能力,因为来自不同背景的人喜欢不同的东西。
将控制结构添加到像Exceptions
这样的2.8就是一个例子:来自Java背景我起初没有得到这些抽象背后的力量,但如果我从功能领域开始那么我可能会马上理解。
我在Scala中的主要问题是对选项类缺乏合理的谓词理解。即使对于某些谓词p
:
opt.isEmptyOr( p ) //does not exist
opt.forall( p )
是等同的,我不喜欢前者从可读性角度来看不存在的事实。同样地:
opt.notEmptyAnd( p ) //does not exist
opt.map(p).getOrElse(false)
在我看来,第二种形式是不必要的! 重复以实现可读性肯定是积极的!
答案 4 :(得分:1)
整个概念太模糊,几乎毫无意义。但是我怀疑,如果你正在使用一种语言,其中只有一种方法可以做任何给定的事情,那么你至少有50%的时间都在哭泣并咬牙切齿。
我(模糊)的感觉是,在任何体面的通用编程语言中,总是有很多方法可以完成大多数事情。
答案 5 :(得分:1)
免责声明:我不知道Scala的作者对此有何看法,我只是表达了我对scala(以及python和ruby)开发人员的看法。
通过Scala API,您没有看到很多方法来做同样的事情。例如,对象的Mutable和Immutable版本具有相同的接口。这样的事情让Scala有了一些连贯性。
作为一种混合语言,通常有两种解决问题的方法:功能和程序方式(foreach vs for)。当然,如果你想使用程序方法,那么你只需要使用Java。
如果你看一下像Lisp和Haskell这样的函数式语言,通常有一种方法可以为你采用的每种方法做到这一点(例如,因为你可以解决递归或列表推导中的问题)。
最后,我相信任何代码都应该是显而易见的。写得很明显。你可以非常灵活(perl / ruby)或严格(python)。 Scala很灵活(想想在方法调用中使用“。”和括号的语法,但是你可以按照你想要的那样严格。个人而言,我喜欢严格的事情,所以我会编写自己的代码,我认为这是显而易见的方式。