I have an exercise where I have to check how many DIFFERENT chars are used in a string.
I don't know how not to count space as a char. I wanted to put if inside the foldLeft, like:
str.foldLeft(lista)((acc, char) => if (char != ' ') char :: acc)
but then it says that required List[Char], found: Any.
What do you generally also think about my function? Could I do it faster/more effectivly?
def countChars(str: String): Int = {
val lista = List[Char]()
val letters = str.foldLeft(lista)((acc, char) => char :: acc)
val result = letters.toSet.size
return result
}
println(countChars("hello world"))
I am not sure if this is "faster or more efficiently", maybe a little bit, but certainly reads better and is more idiomatic :)
str.iterator.filterNot(_.isWhitespace).distinct.size
Or alternatively
str.iterator.distinct.count(!_.isWhitespace)
If you are set on using foldLeft then folding into Set would save you a few ticks compared to creating a list first, and then converting it into set:
str.foldLeft(Set.empty[Char]) {
case (s, ' ') => s
case (s, c) => s + c
}.size
Or you could get rid of the space afterwards:
(str.foldLeft(Set.empty[Char])(_ + _) - ' ').size
First of all you can use Set as accumulator from the start. Then you can use if-else and return current accumulator if current char should not be counted:
val set = Set[Char]()
val uniqueCount = str.foldLeft(set)((acc, char) => if (char != ' ') acc + char else acc)
.size
You can use pattern matching. Then you don't need the if...else.
def countChars(str: String): Int =
str.foldLeft(Set[Char]()){
case (acc, ' ') => acc
case (acc, c) => acc + c
}.size
Related
I am quite new to Scala and functional programming.
I wrote the simple codes as below, which manipulates the string by counting the word.
When the 4th comma-delimitted part is empty then, I concated only three columns, otherwise I concated all the columns including the values as code above.
But I think that it is not quite proper to the functional programming. Because I used the if statement to see the input value contains the value or not.
How to change it to the more scala-like code?
str = "aa,bb,1668268540040,34.0::aa,bb,1668268540040"
val parts = str.split("::")
for (case <- parts) {
val ret = case.map(c => if (c.value.isEmpty) {
c.columnFamily + "," + c.qualifier + "," + c.ts
} else {
c.columnFamily + "," + c.qualifier + "," + c.ts + "," + c.value
})
}
str = "aa,bb,1668268540040,34.0::aa,bb,166826434343"
val parts = str.split("::")
for (part <- parts) {
val elem = part.split(",", 4)
if (elem.length == 4) {
val Array(f, q, t, v) = elem
state.put(f + ":" + q, (v, t.toLong))
} else {
val Array(f, q, t) = elem
state.put(f + ":" + q, ("", t.toLong))
}
}
#LeviRamsey's comment tells you actually everything, but just to make your code more "scala-ish", you should avoid mutable data structures in the first place (what you're doing with state, which I think is a Map object), and use immutable data structures. About your if-else part, it's actually okay in FP, but in Scala, you can use pattern matching on a list, rather than manual length checking and using Arrays. Something like this:
parts.foldLeft(Map.empty[String, (String, Long)]) {
case (state, part) =>
part.split(",", 4).toList match {
case f :: q :: t :: v :: Nil =>
state.updated(f + ":" + q, (v, t.toLong))
case f :: q :: t :: Nil =>
state.updated(f + ":" + q, ("", t.toLong))
case _ => state // or whatever thing you want to do, in case neither 4 nor 3 elements are splitted
}
}
I'm trying to learn functional Scala and working on a simple problem - replace occurrences of \' or \\ contained in a String:
Here is my code so far:
val data : String = "\' this is a test \\ "
data.toCharArray.foldLeft(""){ (x, y) => x match {
case Nil => y :: Nil
case head :: tail =>
if head == '\'' ''
else if head == '\\' ''
else head :: tail
}
There are multiple errors:
I've not understood something fundamental with fold?
Simple examples of foldLeft such as:
val sum = prices.foldLeft(0.0)(_ + _)
are understandable but I'm unsure how to use foldLeft in a context where there is conditions. In the problem I posted the condition being matching on a character.
There are several issues here, starting with some syntactic problems, like missing parentheses around the conditionals. The first real substantive issue is that the initial value (the "" in foldLeft("")) must be the same type as the accumulator, and as the return type. You seem to want a List[Char] as the return type, so you'll need to use something like List.empty[Char] as the initial value.
Next I'd strongly recommend using names like acc and c instead of x and y to indicate more clearly which is the accumulator and which is the current value.
Another issue is that '' also isn't valid Scala syntax—there is no empty character literal. I'll use '_' as the replacement just for the sake of example.
A working implementation might look like this:
val data: String = "\' this is a test \\ "
data.toCharArray.foldLeft(List.empty[Char]) { (acc, c) =>
c match {
case '\'' => acc :+ '_'
case '\\' => acc :+ '_'
case other => acc :+ other
}
}
Which yields:
val data: String = "' this is a test \ "
val res1: List[Char] = List(_, , t, h, i, s, , i, s, , a, , t, e, s, t, , _, )
Which I think is what you're aiming for?
As a footnote, I'm assuming this is just an exercise, but it's worth noting that using a left fold for an operation like this is extremely inefficient, since you're building up a list by appending.
There are several errors in this code:
you haven't closed lambda's bracket
you use List pattern matching on... well string because
x here is result so far (so "" initially) and y are elements of data (chars)
This code should look like this:
val data : String = "\' this is a test \\ "
data.toCharArray.foldLeft("") { (result, ch) =>
if (ch == '\'' || ch == '\\') result
else result + ch
}
I am trying to learn functional programming and Scala, so I'm reading the "Functional Programming in Scala" by Chiusano and Bjarnason. I' m having trouble understanding what fold left and fold right methods do in case of a list. I've looked around here but I haven't find something beginner friendly. So the code provided by the book is:
def foldRight[A,B](as: List[A], z: B)(f: (A, B) => B): B = as match {
case Nil => z
case Cons(h, t) => f(h, foldRight(t, z)(f))
}
def foldLeft[A,B](l: List[A], z: B)(f: (B, A) => B): B = l match {
case Nil => z
case Cons(h,t) => foldLeft(t, f(z,h))(f)
}
Where Cons and Nil are:
case class Cons[+A](head: A, tail: List[A]) extends List[A]
case object Nil extends List[Nothing]
So what do actually fold left and right do? Why are needed as "utility" methods? There are many other methods that use them and I have trouble to understand them as well, since I don't get those two.
According to my experience, one of the best ways to workout the intuition is to see how it works on the very simple examples:
List(1, 3, 8).foldLeft(100)(_ - _) == ((100 - 1) - 3) - 8 == 88
List(1, 3, 8).foldRight(100)(_ - _) == 1 - (3 - (8 - 100)) == -94
As you can see, foldLeft/Right just passes the element of the list and the result of the previous application to the the operation in second parentheses.
It should be also mentioned that if you apply these methods to the same list, they will return equal results only if the applied operation is associative.
Say you have a list of numbers, and you want to add them all up. How would you do that?
You add the first and the second, then take the result of that, add that to the third, take the result of that, add it to the fourth.. and so on.
That's what fold let's you do.
List(1,2,3,4,5).foldLeft(0)(_ + _)
The "+" is the function you want to apply, with the first operand being the result of its application to the elements so far, and the second operand being the next element.
As you don't have a "result so far" for the first application, you provide a start value - in this case 0, as it is the identity element for addition.
Say you want to multiply all of your list elements, with fold, that'd be
List(1,2,3,4,5).foldLeft(1)(_ * _)
Fold has it's own Wikipedia page you might want to check.
Of course there are also ScalaDoc entries for foldLeft and foldRight.
Another way of visualisation of leftFold and rightFold in Scala is through string concatenation, its clearly show how leftFold and rightFold worked, let's see the below example:
val listString = List("a", "b", "c") // : List[String] = List(a,b,c)
val leftFoldValue = listString.foldLeft("z")((el, acc) => el + acc) // : String = zabc
val rightFoldValue = listString.foldRight("z")((el, acc) => el + acc) // : abcz
OR in shorthand ways
val leftFoldValue = listString.foldLeft("z")(_ + _) // : String = zabc
val rightFoldValue = listString.foldRight("z")(_ + _) // : String = abcz
Explanation:
leftFold is worked as ( ( ('z' + 'a') + 'b') + 'c') = ( ('za' + 'b') + 'c') = ('zab' + 'c') = 'zabc'
and rightFold as ('a' + ('b' + ('c' + 'z'))) = ('a' + ('b' + 'cz')) = ('a' + 'bcz') = 'abcz'
If I have a sparse list of numbers:
Vector(1,3,7,8,9)
and I need to generate a string of a fixed size which replaces the 'missing' numbers with a given character that might look like this:
1.3...789
How would I do this in Scala?
Well, I'm not sure the range of the integers. So I'm assuming that they may not fit into a char and used a string. Try this:
val v = Vector(1,3,7,8,9)
val fixedStr = ( v.head to v.last )
.map( i => if (v.contains(i)) i.toString else "." )
.mkString
If you are only dealing with single digits then you may change the strings to chars in the above.
-- edit --
ok, so I couldn't help myself and addressed the issue of sparse vector and wanted to change it to use the sliding function. Figured it does no good sitting on my PC so sharing here:
v.sliding(2)
.map( (seq) => if (seq.size == 2) seq else seq ++ seq ) //normalize window to size 2
.foldLeft( new StringBuilder )( (sb, seq) => //fold into stringbuilder
seq match { case Seq(a,b) => sb.append(a).append( "." * (b - a - 1) ) } )
.append( v.last )
.toString
One way to do this is using sliding and pattern matching:
def mkNiceString(v: Vector[Int]) = {
v.sliding(2).map{
case Seq(a) => ""
case Seq(a,b) =>
val gap = b-a;
a.toString + (if(gap>1) "." * (gap-1) else "")
}.mkString + v.last
}
In the REPL:
scala> mkNiceString(Vector(1,3,7,8,9,11))
res22: String = 1.3...789.11
If the vector is sparse, this will be more efficient than checking the range between the first and the last number.
def padVector(numbers: Vector[Int], placeHolder: String) = {
def inner(nums: Vector[Int], prevNumber: Int, acc: String) : String =
if (nums.length == 0) acc
else (nums.head - prevNumber) match {
// the difference is 1 -> no gap between this and previous number
case 1 => inner(nums.tail, nums.head, acc + nums.head)
// gap between numbers -> add placeholder x times
case x => inner(nums.tail, nums.head, acc + (placeHolder * (x-1)) + nums.head)
}
if (numbers.length == 0) ""
else inner(numbers.tail, numbers.head, numbers.head.toString)
}
Output:
scala> padVector(Vector(1,3,7,8,9), ".")
res4: String = 1.3...789
I am learning Scala and don't understand why the following is not working.
I want to refactor a (tested) mergeAndCount function which is part of a counting inversions algorithm to utilize pattern matching. Here is the unrefactored method:
def mergeAndCount(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = {
if (b.isEmpty && c.isEmpty)
(0, Vector())
else if (!b.isEmpty && (c.isEmpty || b.head < c.head)) {
val (count, r) = mergeAndCount(b drop 1, c)
(count, b.head +: r)
} else {
val (count, r) = mergeAndCount(b, c drop 1)
(count + b.length, c.head +: r)
}
}
Here is my refactored method mergeAndCount2. Which is working fine.
def mergeAndCount2(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = (b, c) match {
case (Vector(), Vector()) =>
(0, Vector())
case (bh +: br, Vector()) =>
val (count, r) = mergeAndCount2(br, c)
(count, bh +: r)
case (bh +: br, ch +: cr) if bh < ch =>
val (count, r) = mergeAndCount2(br, c)
(count, bh +: r)
case (_, ch +: cr) =>
val (count, r) = mergeAndCount2(b, cr)
(count + b.length, ch +: r)
}
However as you can see the second and third case are duplicate code. I therefore wanted to combine them using the disjunction like this:
case (bh +: br, Vector()) | (bh +: br, ch +: cr) if bh < ch =>
val (count, r) = mergeAndCount2(br, c)
(count, bh +: r)
This gives me an error though (on the case line): illegal variable in pattern alternative.
What am I doing wrong?
Any help (also on style) is greatly appreciated.
Update: thanks to your suggestions here is my result:
#tailrec
def mergeAndCount3(b: Vector[Int], c: Vector[Int], acc : (Int, Vector[Int])): (Int, Vector[Int]) = (b, c) match {
case (Vector(), Vector()) =>
acc
case (bh +: br, _) if c.isEmpty || bh < c.head =>
mergeAndCount3(br, c, (acc._1, acc._2 :+ bh))
case (_, ch +: cr) =>
mergeAndCount3(b, cr, (acc._1 + b.length, acc._2 :+ ch))
}
When pattern matching with pipe (|) you are not allowed to bind any variable other than wildcard (_).
This is easy to understand: in the body of your case, what would be the actual type of bh or br for example if your two alternatives match different types?
Edit - from the scala reference:
8.1.11 Pattern Alternatives Syntax: Pattern ::= Pattern1 { ‘|’ Pattern1 } A pattern alternative p 1 | . . . | p n consists of a
number of alternative patterns p i . All alternative patterns are type
checked with the expected type of the pattern. They may no bind
variables other than wildcards. The alternative pattern matches a
value v if at least one its alternatives matches v.
Edit after first comment - you can use the wildcard to match something like this for example:
try {
...
} catch {
case (_: NullPointerException | _: IllegalArgumentException) => ...
}
If you think about that, looking at your case clause, how should the compiler know if in the case body it should be allowed to use ch and cr or not?
This sort of questions make it very hard to make the compiler support disjunction and variable binding in the same case clause, thus this is not allowed at all.
Your mergeAndCount2 function looks quite fine with respect to pattern matching. I think that its most evident problem is not being tail-recursive and thus not running in constant stack space. If you can solve this problem you will probably end with something that is less repetitive as well.
You can rewrite the case expression and move the disjunction to the if part
case (bh +: br, cr) if cr.isEmpty || bh < cr.head =>
val (count, r) = mergeAndCount2(br, c)
(count, bh +: r)
Update:
You can yet simplify a little bit:
#tailrec
def mergeAndCount3(b: Vector[Int], c: Vector[Int],
count: Int = 0, r: Vector[Int] = Vector()): (Int, Vector[Int]) =
(b, c) match {
case (bh +: br, _) if c.isEmpty || bh < c.head =>
mergeAndCount3(br, c, count, bh +: r)
case (_, ch +: cr) =>
mergeAndCount3(b, cr, count + b.length, ch +: r)
case _ => (count, r)
}