最近在学习使用Scala语言做项目,感觉这门语言实在是太优美了!作为一个本科数学、研究生机器学习专业的混合人才(哈哈),这门语言真的是满足了普通计算机编程(告诉计算机怎么做)和函数式编程(告诉计算机做什么)的所有幻想。学了半个多月,根本停不下来!先来做个总结:
- 语法简洁,往往比Java少至少一半的代码量。比如:
- 支持自动类型判断,可以省去很多类型标志。 e.g. val x = 2
- 用伴生对象来生成类,省去new的麻烦。e.g. val cat = Cat("Hello Ketty")
- 不用return,直接把一个块(可以使if...else...块,for循环块等)的值返回。例如一行代码定义函数:def add(x: Int, y: Int): Int = x + y
- 用()来统一函数的参数传递与带参类的构造。对类来说,这种写法其实是语法糖,因为中间有自动的转换机制,使得简洁的代码和底层实现可以分离。
- 程序易读。对比C/C++、Python、Java,Scala是最符合人类理解的程序语言。
- 有几乎完全的函数式风格支持。
- 函数和值一样,是第一等公民。函数也是值,值也可以作为函数。
- 支持高阶函数、Curry化、lambda运算等函数运算概念。
- 函数式风格要求函数尽量无副作用,这样一方面适合做单元测试来验证程序的正确性,另外很适合做并行计算!
- 可以满足大多数OOP编程需求。
- 这里就不展开了。
- Scala兼具科学计算语言(matlab、R、Python等)的易读性与静态语言(C/C++、Java等)的高效率。
正是由于以上优点,我觉得Scala很有潜力成为下一门普及的编程语言。
另外,关于Scala学习曲线,我的建议是:
- 从网上各种博客大概了解Scala的特性,包括上面提到的几条。
- 看书:
- 《Programming in Scala》和《Scala for the Impatient》交替看。前一本是Scala语言创建者写的,非常通俗易懂,包含了Scala的大多数“是什么”和“为什么”;后一本则是总结性的,把Scala的大多少“是什么”告诉你。
- 《Scala in Depth》适合中等水平的Scala程序员阅读。(计划下一步看完)
- 做项目 / 开发程序。
最后,附上《Scala for the Impatient》(中文版《快学Scala》)的所有key notes 以及我做的部分课后题参考答案地址。如果有需要相关电子书的,可以给我发email。
《Scala for the Impatient》key notes
Chapter 1. The Basics
- Using the Scala interpreter
- Defining variables with val and var
- Numeric types
- Using operators and functions
- Navigating Scaladoc
Chapter 2. Control Structures and Functions
- An if expression has a value
- A block has a value — the value of its last expression
- The Scala for loop is like an "enhanced" Java for loop
- Semicolons are (mostly) optional
- The Void type is Unit
- Avoid using return in a function
- Beware of missing = in a function definition
- Exceptions work just like in Java or C++, but you use a "pattern matching" syntax for catch
- Scala has no checked exceptions
Chapter 3. Working with Arrays
- Use an Array if the length is fixed, and an ArrayBuffer if the length can vary
- Don’t use new when supplying initial values
- Use () to access elements
- Use for (elem <- arr) to traverse the elements
- Use for (elem <- arr if …) … yield … to transform into a new array
- Scala and Java arrays are interoperable; with ArrayBuffer, use scala.collection.JavaConversions
Chapter 4. Maps and Tuples
- Scala has a pleasant syntax for creating, querying, and traversing maps
- You need to choose between mutable and immutable maps
- By default, you get a hash map, but you can also get a tree map
- You can easily convert between Scala and Java maps
- Tuples are useful for aggregating values
Chapter 5. Classes
- Fields in classes automatically come with getters and setters
- You can replace a field with a custom getter / setter without changing the client of a class — that is the "uniform access principle"
- Use the @BeanProperty annotation to generate the JavaBeans getXxx / setXxx methods
- Every class has a primary constructor that is "interwoven" with the class definition. Its parameters turn into the fields of the class. The primary constructor excuses all statements in the body of the class
- Auxiliary constructors are optional. That are called this.
Chapter 6. Objects
- Use Objects for singletons and utility methods
- A class can have a companion object with the same name.
- Objects can extend classes or traits
- The apply method of an object is usually used for constructing new instance of the companion class
- To avoid the main method, use an object that extends that App trait
- You can implement enumerations by extending the Enumeration object
Chapter 7. Packages and imports
- Packages nest just like inner classes
- Package paths are not absolute
- A chain x.y.z in a package clause leaves the intermediate packages x and x.y invisible
- Package statements without braces at the top of the file extend to the entire file
- A package object can hold functions and variables
- Import statements can import packages, classes, and objects
- Import statements can be anywhere
- Import statements can rename and hide menbers
- java.lang, scala, and Predef are always imported
Chapter 8. Inheritance
- The extends and final keywords are as in Java.
- You must use override when you override a method
- Only the primary constructors can all the primary superclass constructor
- You can override fields
Chapter 9. Files and Regular Expressions
- Source.fromFile(…).getLines.toArray yields all lines of a file
- Source.fromFile(…).mkString yields the file contents as a string
- To convert a string into a number, use the toInt or toDouble method
- Use the Java PrintWriter to write text files
- "regex".r is a Regex object
- Use """…""" if your regular expression contains backslashes or quotes
- If a regex pattern has groups, you can extract their contents using the syntax for (regex(var_1,…,var_n) <- string )
Chapter 10. Traits
- A class can implement any number of traits
- Traits can require that implementing classes have certain fields, methods, or superclasses
- Unlike Java Interface, a Scala trait can provide implementations of methods and fields
- When you layer multiple traits, the order matters — the trait whose methods execute first goes to the back
Chapter 11. Operators
- Identifiers contain either alphanumeric or operator characters
- Unary and binary operators are method calls
- Operator precedence depends on the first character, associativity on the last
- The apply and update methods are called when evaluating expr(args)
- Extractors extract tuples or sequences of values from an input
Chapter 12. Higher-Order Functions
- Functions are "first-class citizens" in Scala, just like numbers
- You can create anonymous functions, usually to give them to other functions
- A function argument specifies behavior that should be executed later
- Many collection methods take function parameters, applying a function to the values of the collections
- There are syntax shortcuts that allow you to express function parameters in a way that is short and easy to read
- You can create functions that operate on the blocks of code and look much like the build-in control statement
Chapter 13. Collections
- All collections extend the Iterable trait
- The three major categories of collections are sequences, sets, and maps.
- Scala has mutable and immutable versions of most collections
- A Scala list is either empty, or it has a head and a tail which is again a list
- Sets are unordered collections
- Use a LinkedHashSet to retain the insertion order or a SortedSet to iterate in sorted order
- + adds an elements to an unordered collection; +: and :+ prepend or append to a sequence; ++ concatenates two collections; - and -- remove elements
- The Iterable and Seq traits have dozens of useful methods for common operations. Check them out before writing tedious loops.
- Mapping, folding, and zipping are useful techniques for applying a function or operation to the elements of a collection
Chapter 14. Pattern Matching and Case Classes
- The match expression is a better switch, without fall-through
- If no pattern matches, a MatchError is thrown. Use the case _ pattern to avoid that
- A pattern can include an arbitrary condition, called a guard
- You can match on the type of an expression; prefer this over isInstanceOf / asInstanceOf
- You can match patterns of arrays, tuples, and case classes, and bind parts of the pattern to variables
- In a for expression, non matches are silently skipped
- A case class is a class for which the compiler automatically produce the methods that are needed for pattern matching
- The common superclass in a case class hierarchy should be sealed
- Use the Option type for values that may or may not be present — it is safer than using null
Chapter 15. Annotations
- You can annotate classes, methods, fields, local variables, parameters, expressions, type parameters and types.
- With expressions and types, the annotation follows the annotated item
- Annotation have the form @Annotation, @Annotation(value), or @Annotation(name1= value1, …)
- @volatile, @transient, @strictfp, and @native generate the equivalent Java modifiers
- Use @throws to generate Java-compatible throws specifications
- The @tailrec annotation lets you verify that a recursive function uses tail call optimization
- The assert function takes advantage of the @elidable annotation. You can optionally remove assertions from your Scala program
- Use the @deprecated annotation to mark deprecated features.
Chapter 16. XML Processiong
- XML literals <like> this </like> are of type NodeSeq
- You can embed Scala code inside XML literals
- The child property of a Node yield the child nodes
- The attributes property of a Node yields a MetaData object containing the node attributes.
- The \ and \\ operators carry out XPath-Like matches
- You can match mode patterns with XML literals in case clauses
- Use the RuleTransformer with RewriteRule instance to transform descendants of a node
- The XML object interfaces with Java XML methods for loading and saving
- The ConstructingParser is an alternate parser that preserves comments and CDATA sections
Chapter 17. Type parameters
- Classes, traits, methods, and functions can have type parameters
- Place the type parameters after the name, enclosed in square brackets
- Type bounds have the form T <: UpperBound, T >: LowerBound, T <% ViewBound, T : ContextBound
- You can restrict a method with a type constraint such as (implicit ev: T <: < UpperBound)
- Use +T (covariance) to indicate that a generic type’s subtype relationship is in the same direction as the parameter T, or -T (contravariance) to indicate the reverse direction
- Covariance is appropriate for parameters that denote outputs, such as elements in an immutable collection.
- Contravariance is appropriate for parameters that denote inputs, such as function arguments.
Chapter 18. Advanced types
- Singleton types are useful for method chaining and methods with object parameters
- A type projection includes inner class instances for all objects of an outer class.
- A type alias gives a short name for a type.
- Structural types are equivalent to "duck typing"
- Existential types provide the formalism for wildcard parameters of generic types.
- Use a self type declaration to indicate that a trait requires another type.
- The "cake pattern" uses self types to implement a dependency injection mechanism
- An abstract type must be made concrete in a subclass
- A higher-kinded type has a type parameter that is itself a parameterized type.
Chapter 19. Parsing
- Alternatives, concatenation, options, and repetitions in a grammar turn into |, ~, opt, and rep in Scala combinator parsers.
- With RegexParsers, literal strings and regular expressions match tokens.
- Use ^^ to process parse results.
- Use pattern matching in a function supplied to ^^ to take apart ~ result.
- Use ~> and <~ to discard tokens that are no longer needed after matching.
- The respell combinator handles the common case of repeated items with a seperator
- A token-based parser is useful for paring languages with reserved words and operations. Be prepared to define your own lexer.
- Parsers are functions that consume a reader and yield a parse result: success, failure, or error.
- The Failure result provides the details for error reporting.
- You may want to add failure clauses to your grammar to improve the quality of error message.
- Thanks to operator symbols, implicit conversions, and pattern matching, the parser combinator library makes parser writing easy for anyone who understands context-free grammars. Even if you don’t feel the urge to write your own parsers, you may find this an interesting case study for an effective domain-specific language.
Chapter 20. Actors
- Extend the Actor class and provide an act method for each other.
- To send a message to an actor, use actor ! message.
- Message sending is asynchronous: "send and forget."
- To receive messages, an actor calls receive or react, usually in a loop.
- The argument to receive / react is a block of case clauses (technically, a partial function)
- Actors should never share state. Always send data using messages.
- Don’t invoke methods on actors. Communicating by sending messages.
- Avoid synchronous messaging — that is, unlink sending a message and waiting for a reply.
- Actors can share threads by using react instead of receive, provided the control flow of the message handler is simple.
- It is OK to let actors crash, provided you have other actors that monitor their demise. Use linking to set up monitoring relationships.
Chapter 21. Implicits
- Implicit conversions are used to convert between types.
- You must import implicit conversions so that they are in scope as single identifiers.
- An implicit parameter list requests objects of a given type. They can be obtained from implicit objects that are defined as single identifiers in scope, or from the companion object of the desired type.
- If an implicit parameter is a single-argument function, it is also used as an implicit conversion.
- A context bound of a type parameter requires the existence of an implicit object of the given type.
- If it is possible to locate an implicit object, this can serve as evidence that a type conversion is valid.
Chapter 22. Delimited Continuations
- A continuation lets you go back to a previous point in a program.
- You can capture a continuation in a shift block.
- A contination function extends until the end of the enclosing reset block.
- A continuation is the "reset of the computation" from the expression containing the shift to the end of the enclosing reset, with the shift replaced by a "hole"
- When you call a continuation with an argument, the "hole" is set to the argument.
- Code containing shift expressions is rewritten in "continuation-passing stype", or CPS, up to the enclosing reset.
- A method containing a shift without a reset must be annotated with a CPS annotation.
- Continuation can be used to turn a recursive visit of a tree structure into an iteration.
- Continuations can undo the "inversion of control" in a web or GUI application.
部分《快学Scala》课后题答案(还在更新中):
https://github.com/fengfu-chris/Scala-for-the-Impatient-Exercises