Scala Code — I fail to understand [closed]
I've got part of code from friend and I'm trying to understand it and write it in some other way. "gotowe" is a sorted list of ("2011-12-22",-600.00)
elements
val wartosci = gotowe.foldLeft (List(initial_ballance)){
case ((h::t), x) => (x._2 + h)::h::t
case _ => Nil
}.reverse
That is quite okay but how with this usage of foldLeft? (I've put all extra necessary lines):
val max = wartosci.max
val min = wartosci.min
val wychylenie = if(math.abs(min)>max){math.abs(min)}else{max}
def scale(x: Double) =
(x / wychylenie) * 500
def point(x: Double) =
{val z:Int = (500 - x).toInt
z}
val (points, _) = wartosci.foldLeft(("", 1)){case ((a, i), h) => (a + " " + (i * 4) + "," + point(scale(h)), i + 1)}
when I print points I've got a list of values, and don't know why not something like pairs of values
There are a couple of concepts at work here, which we'll examine in turn to work out what's going on:
- foldLeft
- Pattern matching
Let's first look at the definition of foldLeft:
def foldLeft [B] (z: B)(f: (B, A) ⇒ B) : B
Applies a binary operator to a start value and all elements of this list, going left to right.
Returns the result of inserting op between consecutive elements of this list, going left to right with the start value z on the left: op(...op(z, x1), x2, ..., xn) where x1,..., xn are the elements of this list.
So, in your example we're taking a list of Tuple2[String, Float] (or something like that) and folding it into the value z, which in this case is a List containing one element, initial_balance
.
Now, our f
in this case is the code inside the braces. It uses pattern matching to compose a partial function from the pair (b,a)
- where in this case b
is the 'cumulative result' and a
is the next item in the list. This is the crux of what a fold does - it collapses the list into a value, using specific rules governing how to add each element at a time.
What is pattern matching / a partial function? Pattern matching is a very powerful technique for conditioning on and extracting things from input data. We give it something to look for - the case
part of the expression - and tell it how to deal with it following the =>
. The power of this is that the case
expression doesn't just match, say, numbers or specific strings as might the switch statement in java, but can match, for example, Lists of a certain length, or email addresses, or specific tuples. Even more, you can use it to automatically get certain parts of the match - the domain of the email address, the third element of the list etc.
We'll look at the first pattern:
case ((h::t), x) => (x._2 + h)::h::t
The left hand side (before the =>
) is used to match the value we're looking for and extract the specific pieces we care about. In this case, we're looking for a tuple where the first element is a list consisting of a head (h
) and a tail(t
), and the second element is just the next element of the list. The h::t
is an extractor pattern - it's matching the object ::(h,t) which constructs a List
by prepending h
onto an existing List
t.
When we've matched this, we follow the instructions to the right of the =>
to fold x
into the cumulative value. To do this, we take the right hand side of the date/value tuple (the ._2
), add it to the last value in the list (the head), and then push itself on to the head of the list. You'll notice this is using the same syntax as we used in the pattern match - using ::
to prepend elements to a List
.
The effect in this case is to create a running total of what's going on.
The second case doesn't really do much - it's a catch all case, but as this is being used in a fold it should never get called - we're always going to return something that looks like ((h::t), x)
.
Finally, we reverse the whole thing! So what we're left with is a list of balances after each transaction, running from oldest to youngest.
This is quite simple. It's just the matter of the assignment. You have this:
val (points, _) = wartosci.foldLeft(("", 1)){...}
What is inside {...}
is not relevant. The first parameter of foldLeft
will determine the type of its result. Since it is ("", 1)
, it will return a (String, Int)
tuple.
Now, you assign it to (points, _)
. An assignment like this is also a pattern match. It is like you had written this:
var tmp: (String, Int) = _
val tmp: (String, Int) = wartosci.foldLeft(("", 1)){...} match {
case (x, y) => tmp = (x, y)
}
val points = tmp._1
So, points
only gets assigned the String
.
精彩评论