Scala Seq's find - wrong number of parameters; expected = 1 - scala

I have a Seq val that is populated with case class instances. I am then trying to use the find method in order to find the first option matching my criteria. Here is the code:
val week = weeks.find(now >= _.start && now <= _.end).headOption.map( _.week).getOrElse{0}
This is giving me an error:
wrong number of parameters; expected = 1
am I using the find method incorrectly above? The case class in the event it helps that weeks is populated with has the following definition:
case class Period(week: Int, start: DateTime, end: DateTime)

You can only use _ once per parameter, so scala thinks you're giving find a method that takes two parameters and it's telling you that it only takes a method with one parameter. This should work instead:
val week = weeks.find(p => now >= p.start && now <= p.end).headOption
.map( _.week).getOrElse{0}
As a side note, you don't need to use headOption because find is already returning an option of the first instance that matches your predicate. Additionally, instead of map and getOrElse you should use a fold as it has much stronger type safety:
val week2 = weeks.find(p => now >= p.start && now <= p.end).fold(0)( _.week)

Related

Convert an Int type to a tuple identifier in scala?

Is it possible to convert an Int to a tuple identifier (in scala)? So for a working example suppose I had this:
val testTuple = ("Hector", "Jonas", "Javi")
val id = 2
println(testTuple._id) // does not work as it tries 'num' as a name parameter
I can see that tuple elements can be accessed by the order in which they appear - much like an index (except the first value is 1 rather than 0), e.g. testTuple._1 // is Hector would work as described here among other places.
So how can this be done? Many thanks
You can use testTuple.productElement(id - 1). But note that this returns Any.
NO, You can not do this. _n is a member of tuple<n> and it automatically equals to the size of tuple. According to the notes:
For each TupleN type, where 1 <= N <= 22, Scala defines a number of
element-access methods.
Ex:
val data = (4,3,2)
val sum = data._1 + data._2 + data._3
For more information, you can see Scala Tuples.
Thanks.

Rounding numeric parameter types in Scala

I'm experimenting with a method in Scala which is attempting to round numbers depending on how big they are, e.g. if the value is below 1 then it makes sense to round it by at least one decimal point; rather than remaining an integer. Here's what I'm trying:
def roundSmart[A](num: A)(implicit numeric: Numeric[A]) = num match {
case num if num < 1 => numeric.toDouble(num)
case _ => numeric.toInt(num)
}
Which throws this error:
value < is not a member of type parameter A
Of course the parameters need to accept a range of parameter types within the function signature as it may be taking integers or doubles so this has added a further complexity.
I've taken the conditional statement on the first case from this answer -
Using comparison operators in Scala's pattern matching system and am eager to use a Functional Programming approach. Perhaps there is also some in-built scala function like Math.round() that could help to round the second condition rather than remove any decimals. Thanks!
TL;DR : every numeric methods you'll need are inside numeric
The error is self-explanatory: your type A (which is generic) does not have a < method.
However, what you have is a typeclass with all numeric methods in it, so you should probably import them:
import numeric._
This imports (among other things) implicit conversion to OrderingOps, which has the desired method. But this comparison cannot work, since 1 is not an A, it's an Int!
The solution is simply to use fromIntof numeric.
While we're at it, you will have unexpected results for input such as -234.2e123... You'd better do your test on the absolute value of your number (abs is also a method of numeric).
Also, if you want to do a simple test, there is no need to use pattern matching, a if else statement is enough
def roundSmart[A](num: A)(implicit numeric: Numeric[A]) = {
import numeric._
if (abs(num) < fromInt(1)) toDouble(num) else toInt(num)
}

why scala lambda with _ can't using && to combine two bool expression

As far as I understand .
_ is a short lambda to omit a=>
i find this code (can find here scala-function-true-power)
val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
val size = file.filter(_.contains("warn")).filter(_.contains("2013")).size
//val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
val size2 = file.filter( a=> a.contains("warn") && a.contains("2013")).size
println("cat file | grep 'warn' | grep '2013' | wc : " +size )
the line to get size1 has syntax error,looks like it can't recognize the "_" ,it's not a element in fileList.
but i use a=>,the normal kind,it works good .
so,why the scala work by this way?
is there more difference in _ and a=> ?
In scala, any _ placeholder is matched against the passed arguments in the context of calling function. So for example if the signature of the function you are trying to use is f : A ⇒ B and you are calling something like collectionOfFunctA.map(_.f) - Scala compiler will infer the correct type of the function and will use the first underscore to put the actual item from a collection and call the function f over it. But if you will try to write it as collectionOfFunctA.map(_.f + _.size) - that will fail, because Scala compiler will pick up the first placeholder as of type that has function f defined, and the second underscore will not match any function in the context. So it will expect to have a function that takes two parameters instead of one.
More on this
As jdevelop says, but here in the words of the compiler/REPL:
scala> val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
^
You see that hint: for expanded function ((x$1, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
It is expecting 2 parameters while there is just one.
You can think of the place holder as being matched with the lambda's arguments positionnally.
The first occurrence of the _ is matched with the first argument, the second occurence is matched with the second argument, etc.
As the other answers have shown, this means that using the placeholder twice will be desugared as trying to pass a lamba with 2 arguments to the filter which only expects one.
In your example :
val size = file.filter(_.contains("warn") && _.contains("2013")).size
would be desugared as
val size = file.filter((a,b)=>a.contains("warn") && b.contains("2013")).size
which will not compile since filter expects a predicate p: A => Boolean
Now, a reason the placeholder is matched positionnally is to avoid ambiguity in lambdas with more than one argument.
How can the compiler guess the correct implementation for the following case if the place holder can be reused multiple times for the same argument:
file.fold("")(_++_)
Should it be desugared as :
file.fold("")((a,b)=> a++b )
or as
file.fold("")((a,b)=> a++a )
or as
file.fold("")((a,b)=> b++b )
and worse, what would you expect for
file.fold("")(_++_++_)
There is no general way for the compiler to infer the correct implementation.
One might argue for relaxing the constraint when the expected lambda only accepts one argument. I suggest doing a more detailed research before taking the first steps to the scala improvement process as it seems likely that this particular design decision has been challenged and explained before.
If you are worried about the performance of iterating over the list twice (which is the case when you write)
file.filter(_.contains("warn")).filter(_.contains("2013")).size
In theory it should be possible for the compiler to detect that both filters can be applied within the same iteration.
In scala, the collections are eager by default but you can get the lazy evaluation by using views.
The current implementation has known issues which are being worked on. Other collection implementations in scala are actively being developed to be able to combine transformations and computations by default (see psp-std for example)

Sort List according to more than only constraint in Scala

I am desperately trying to find a way to sort a List of strings, where the strings are predefined identifiers of following form: a1.1, a1.2,..., a1.100, a2.1, a2.2,....,a2.100,...,b1.1, b1.2,.. and so on, which is alread the correct ordering. So each identifier is first ordered by its first character (descending alphabetic order) and within this ordering descending ordered by consecutive numbers. I have tried sortWith by providing a sorting function specifying the above rule for all two consecutive list members.
scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => a.take(1) < b.take(1) && a.drop(1).toDouble < b.drop(1).toDouble)
res2: List[java.lang.String] = List(a1.102, a1.1, b2.2, b2.1)
This is not the ordering I expected. However, by swapping the ordering of the expressions, as
scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => (a.drop(1).toDouble < b.drop(1).toDouble && a.take(1) < b.take(2)))
res3: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2)
this indeed gives me (at least for this example) the desired ordering, which I do not understand neither.
I would be so thankful, if somebody could give me a hint what exactly is going on there and how I can sort lists as I wish (with a more complex boolean expression than only comparing < or >). A further question: The strings I am sorting (in my example) are actually keys from a HashMap m. Will any solution effect sorting m by its keys within
m.toSeq.sortWith((a: (String, String), b: (String, String)) => a._1.drop(1).toDouble < b._1.drop(1).toDouble && a._1.take(1) < b._1.take(1))
Many thanks in advanced!
Update: I misread your example—you want a1.2 to precede a1.102, which the toDouble versions below won't get right. I'd suggest the following instead:
items.sortBy { s =>
val Array(x, y) = s.tail.split('.')
(s.head, x.toInt, y.toInt)
}
Here we use Scala's Ordering instance for Tuple3[Char, Int, Int].
It looks like you have a typo in your second ("correct") version: b.take(2) should doesn't make sense, and should be b.take(1) to match the first. Once you fix that, you get the same (incorrect) ordering.
The real problem is that you only need the second condition in the case where the numbers match. So the following works as desired:
val items = List("a1.102", "b2.2", "b2.1", "a1.1")
items.sortWith((a, b) =>
a.head < b.head || (a.head == b.head && a.tail.toDouble < b.tail.toDouble)
)
I'd actually suggest the following, though:
items.sortBy(s => s.head -> s.tail.toDouble)
Here we take advantage of the fact that Scala provides an appropriate Ordering instance for Tuple2[Char, Double], so we can just provide a transformation function that turns your items into that type.
And to answer your last question: yes, either of these approaches should work just fine with your Map example.
Create a tuple containing the string before the "." and then the integer after the ".". This will use a lexicographic order for the first part and an order on the integer for the second part.
scala> val order = Ordering.by((s:String) => (s.split("\\.")(0),s.split("\\.")(1).toInt))
order: scala.math.Ordering[String] = scala.math.Ordering$$anon$7#384eb259
scala> res2
res8: List[java.lang.String] = List(a1.5, a2.2, b1.11, b1.8, a1.10)
scala> res2.sorted(order)
res7: List[java.lang.String] = List(a1.5, a1.10, a2.2, b1.8, b1.11)
So consider what happens when your sorting function is passed a="a1.1" and b="a1.102".
What you'd like is for the function to return true. However, a.take(1) < b.take(1) returns false, so the function returns false.
Think about your cases a bit more carefully
if the prefix is equal, and the tails are ordered properly, then the arguments are ordered properly
if the prefixes are not equal, then the arguments are ordered properly only if the prefixes are.
So try this instead:
(a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1)
And that returns the proper ordering:
scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1))
res8: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2)
The reason it worked for you with the reversed ordering was luck. Consider the extra input "c0" to see what was happening:
scala> List("c0", "a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => (a.drop(1).toDouble < b.drop(1).toDouble && a.take(1) < b.take(2)))
res1: List[java.lang.String] = List(c0, a1.1, a1.102, b2.1, b2.2)
The reversed function sorts on the numeric part of the string first, then on the prefix. It just so happens that your numeric ordering you gave also preserved the prefix ordering, but that won't always be the case.

Scala placeholder syntax

There is something that I can't quite understand hope someone can shed some light..
I have Seq[String]
val strDeps: Seq[String] = ...
and I tried to sort it on the reverse of the using the sortWith method and I get the following error.
scala> print(strDeps.sortWith(_.reverse.compareTo(_.reverse) < 0) mkString ("\n"))
<console>:15: error: wrong number of parameters; expected = 2
print(strDeps.sortWith(_.reverse.compareTo(_.reverse) < 0) mkString ("\n"))
^
But when I try sort it without doing a reverse it works fine.
scala> print(strDeps.sortWith(_.compareTo(_) < 0) mkString ("\n"))
// this is fine
Also it works fine without the placeholder syntax
scala> print(strDeps.sortWith((a,b) => a.reverse.compareTo(b.reverse) < 0) mkString ("\n"))
// this works fine too
_ expands only to the smallest possible scope.
The inner _.reverse part is already interpreted as x => x.reverse therefore the parameter is missing inside sortWith.
compareTo(_)
Is a partially applied method. It just means "compareTo, but without applying the first parameter". Note that _ is not a parameter. Rather, it indicates the absence of a parameter.
compareTo(_.reverse)
Is a method taking an anonymous function as parameter, the parameter being _.reverse. That translates to x => x.reverse.