On this page
I’ve been working through the Coursera course Principles of Reactive Programming the past few days. I hadn’t realised it expected a solid foundation in at least one functional language, so I’m cramming. I happened to have an unread copy of Seven Languages in Seven Weeks lying around, so I’m using the course as the kick I needed to actually crack it open — though just the Scala chapter. Today I’m on Day 2, and the headline topic is Collections. This post is mostly my notes on the exercises.
Find
How to work with files in Scala
Because Scala can use any object from Java, you can lean on Java’s file APIs — java.io.File is one of them. Example:
import java.io._
object Test {
def main(args: Array[String]) {
val writer = new PrintWriter(new File("test.txt"))
writer.write("Hello Scala")
writer.close()
}
}
To read a file from disk, Scala has its own library support. Here’s a snippet that reads a local file and prints its contents:
import scala.io.Source
object Test {
def main(args: Array[String]) {
println("Following is the content read:" )
Source.fromFile("test.txt" ).foreach{
print
}
}
}
What’s the difference between a closure and a code block?
I had to piece together the closure concept from the web plus my own understanding.
Closure
A closure is a block of code that can contain free variables (not bound to any specific object). These variables aren’t defined inside the block or in any global context, but rather in the environment where the block was defined (i.e. they’re local variables of an enclosing scope). The name “closure” comes from combining two things: the block of code to be executed (whose free variables, and the objects they reference, aren’t released because they’re captured inside the block) and the computation environment (scope) that supplies the bindings for those free variables. You’ll find closure support, to varying degrees, in Scala, Scheme, Common Lisp, Smalltalk, Groovy, JavaScript, Ruby, Python, Go, Lua, Objective-C, and Java (Java 8+). — Baidu Baike
Code block
A code block, syntactically, is a unit made up of logically grouped statements:
if (Condition) {
// one Block
} else {
// another Block
}
Do
Using foldLeft to compute the total length of all strings in a list
About foldLeft and foldRight
foldLeft
Scala’s foldLeft is declared in GenTraversableOnce.scala:
/** Applies a binary operator to a start value and all elements of this $coll,
* going left to right.
*
* $willNotTerminateInf
* $orderDependentFold
*
* @param z the start value.
* @param op the binary operator.
* @tparam B the result type of the binary operator.
* @return the result of inserting `op` between consecutive elements of this $coll,
* going left to right with the start value `z` on the left:
* {{{
* op(...op(z, x_1), x_2, ..., x_n)
* }}}
* where `x,,1,,, ..., x,,n,,` are the elements of this $coll.
*/
def foldLeft[B](z: B)(op: (B, A) => B): B
and implemented in TraversableOnce.scala:
def foldLeft[B](z: B)(op: (B, A) => B): B = {
var result = z
this foreach (x => result = op(result, x))
result
}
It takes two arguments, z and op: z is of type B, and op is a function whose return type is also B. There’s also a version that uses the /: operator:
def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
which is really just foldLeft under a different name. An example:
scala> val list= List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> list.foldLeft(0)((sum, value) => sum + value)
res6: Int = 6
foldLeft is handed an initial value plus a block taking two parameters — sum and value. foldLeft starts sum at 0 and walks the list, accumulating each element into sum. So that single line sums the list.
foldRight
foldRight is implemented like this:
def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
def foldRight[B](z: B)(op: (A, B) => B): B =
reversed.foldLeft(z)((x, y) => op(y, x))
So foldRight is actually defined in terms of foldLeft — with an extra reversed step. reversed is an internal method on the trait:
// @tparam A the element type of the collection
...
// for internal use
protected[this] def reversed = {
var elems: List[A] = Nil
self foreach (elems ::= _)
elems
}
So foldRight reverses the collection first and then does a foldLeft over it. Note that the parameter order in the block passed to foldRight is flipped — the list element comes first (that’s my read of it, anyway). And because of the extra reversal step, foldRight is a bit less efficient than foldLeft. Example:
scala> val list = List(1, 2, 3, 4, 5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.foldRight(100)((elem, sum) => sum - elem)
res10: Int = 85
Back to the original problem
Once you understand foldLeft, summing the string lengths in a list falls right out:
scala> val numbers = List("one","two","three")
numbers: List[String] = List(one, two, three)
scala> val length = (0 /: numbers){(sum, elem) => sum + elem.length}
length: Int = 11
scala> val length2 = numbers.foldLeft(0)((sum, value)=> sum + value.length)
length2: Int = 11
/: and foldLeft both get us there (and they’re really the same thing under the hood).
Write a Censor trait with a method that swaps “Pucky” and “Beans” for “Shoot” and “Darn”. Use a map to store the curse words and their replacements.
import scala.collection.mutable.Map
trait Censor {
val curseWords = Map("Pucky" -> "Shoot", "Beans" -> "Darn")
def censor(s: String) = curseWords.foldLeft(s)((prev, curr) => prev.replaceAll(curr._1, curr._2))
}
class Text(words: String) extends Censor {
def origin = words
def transform = censor(words)
}
val text = new Text("Pucky && Beans")
println("Original String: " + text.origin)
println("Replaced String: " + text.transform)
输出结果:
$ scala Censor.trait
Original String: Pucky && Beans
Replaced String: Shoot && Darn
Load the curse words and their replacements from a file.
With the earlier discussion of file I/O in Scala, this one’s easy — you just need to persist the map’s key/value pairs on disk and read them back. Here’s the read side:
import scala.collection.mutable.Map
trait fileRead {
val curseWords = Map[String, String]()
io.Source.fromFile("./files/censor.txt").getLines().foreach {
(line) => val subWords = line.split(":")
curseWords += (subWords(0) -> subWords(1))
}
println("After Read, curseWords are")
curseWords.foreach(p => println(">>> key=" + p._1 + ", value=" + p._2 + " <<<"))
}
class tryFile extends fileRead
val fileObj = new tryFile
And the write side:
import scala.collection.mutable.Map
import java.io._
trait fileWrite {
val curseWords = Map("Pucky"->"Shoot", "Beans"->"Darn")
def writeMapToFile {
println("Start to write map to file")
val writer = new PrintWriter(new File("./files/curseToWrite.txt"))
curseWords.foreach{
(elem) => writer.write(elem._1 + ":" + elem._2)
writer.write("\n")
}
writer.close()
println("end to write map to file")
}
}
class tryFile extends fileWrite
val fileObj = new tryFile
fileObj.writeMapToFile
That’s it! I’ll add the closure example later tonight when I’m back from work.