I'm looking for a Scala implementation of Haskell's groupBy.
The behavior should be like this:
isD :: Char -> Bool
isD c = elem c "123456789-_ "
groupBy (\a b -> isD a == isD b) "this is a line with 0123344334343434343434-343 3345"
["this"," ","is"," ","a"," ","line"," ","with"," 0123344334343434343434-343 3345"]
I tried the Scala groupBy function, however it only takes a function of one argument, instead of Haskell's 2. I also looked at partition, however it only returns a tuple.
The function I'm looking for should group each consecutive element matching a predicate.
Questions like this seem to come up quite often, which is a good indication IMO that Rex Kerr's groupedWhile
method should be included in the standard collections library. However if you don't want to copy / paste that into your project...
I like your recursive solution, but it doesn't actually output the right thing (ie Strings), so here's how I'd change it:
def groupBy(s: String)(f: (Char, Char) => Boolean): List[String] = s match {
case "" => Nil
case x =>
val (same, rest) = x span (i => f(x.head, i))
same :: groupBy(rest)(f)
}
Then, take your function and try it in the REPL:
val isD = (x: Char) => "123456789-_ " contains x
groupBy("this is a line with 0123344334343434343434-343 3345")(isD(_) == isD(_))
The result is a List[String]
, which is presumably what you really wanted.
Used this for now, thanks to the answers:
def groupByS(eq: (Char,Char) => Boolean, list: List[Char]): List[List[Char]] = {
list match {
case head :: tail => {
val newHead = head :: tail.takeWhile(eq(head,_))
newHead :: groupByS(eq, tail.dropWhile(eq(head,_)))
}
case nil => List.empty
}
}
this can probably be improved upon ;)
It's surely can't be too difficult to translate the Haskell version into Scala. Here's the Haskell definition of groupBy
. It uses span
; I don't know offhand whether there's an equivalent to span
in Scala or whether you'll need to translate the Haskell definition of span
as well.
def partitionBy[T, U](list: List[T])(f: T => U ): List[List[T]] = {
def partitionList(acc: List[List[T]], list: List[T]): List[List[T]] = {
list match {
case Nil => acc
case head :: tail if f(acc.last.head) == f(head) => partitionList(acc.updated(acc.length - 1, head :: acc.last), tail)
case head :: tail => partitionList(acc ::: List(head) :: Nil, tail)
}
}
if (list.isEmpty) List.empty
else partitionList(List(List(list.head)), list.tail)
}
partitionBy("112211".toList)(identity)
//res: List[List[Char]] = List(List(1, 1), List(2, 2), List(1, 1))
val l = List("mario", "adam", "greg", "ala", "ola")
partitionBy(l)(_.length)
//res: List[List[String]] = List(List(mario), List(greg, adam), List(ola, ala))
My version, just messing around -- not too sure about it. I know Haskell better than Scala but trying to learn Scala:
object GroupByTest extends App {
val ds = Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', ' ')
def isD(d: Char) = ds contains d
def hgroupBy[A](op: A => (A => Boolean), a: List[A]): List[List[A]] =
a match {
case Nil => List.empty
case x :: xs =>
val t = xs span op(x)
(x :: t._1) :: hgroupBy(op, t._2)
}
val lambda: Char => Char => Boolean = x => y => isD(x) == isD(y)
println(hgroupBy(lambda, "this is a line with 0123344334343434343434-343 3345".toList))
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.