scala how to reduce while n > 0 - scala

I am wondering if there's a way to deal with a while (n > 0) loop in a more functional way, I have a small Scala app that counts the number of digits equal to K from a range from 1 to N:
for example 30 and 3 would return 4 [3, 13, 23, 30]
object NumKCount {
def main(args: Array[String]): Unit = {
println(countK(30,3))
}
def countKDigit(n:Int, k:Int):Int = {
var num = n
var count = 0
while (num > 10) {
val digit = num % 10
if (digit == k) {count += 1}
num = num / 10
}
if (num == k) {count += 1}
count
}
def countK(n:Int, k:Int):Int = {
1.to(n).foldLeft(0)((acc, x) => acc + countKDigit(x, k))
}
}
I'm looking for a way to define the function countKDigit using a purely functional approach

First expand number n into a sequence of digits
def digits(n: Int): Seq[Int] = {
if (n < 10) Seq(n)
else digits(n / 10) :+ n % 10
}
Then reduce the sequence by counting occurrences of k
def countKDigit(n:Int, k:Int):Int = {
digits(n).count(_ == k)
}
Or you can avoid countKDigit entirely by using flatMap
def countK(n:Int, k:Int):Int = {
1.to(n).flatMap(digits).count(_ == k)
}

Assuming that K is always 1 digit, you can convert n to String and use collect or filter, like below (there's not much functional stuff you can do with Integer):
def countKDigit(n: Int, k: Int): Int = {
n.toString.collect({ case c if c.asDigit == k => true }).size
}
or
def countKDigit(n: Int, k: Int): Int = {
n.toString.filter(c => c.asDigit == 3).length
}
E.g.
scala> 343.toString.collect({ case c if c.asDigit == 3 => true }).size
res18: Int = 2
scala> 343.toString.filter(c => c.asDigit == 3).length
res22: Int = 2

What about the following approach:
scala> val myInt = 346763
myInt: Int = 346763
scala> val target = 3
target: Int = 3
scala> val temp = List.tabulate(math.log10(myInt).toInt + 1)(x => math.pow(10, x).toInt)
temp: List[Int] = List(1, 10, 100, 1000, 10000, 100000)
scala> temp.map(x => myInt / x % 10)
res17: List[Int] = List(3, 6, 7, 6, 4, 3)
scala> temp.count(x => myInt / x % 10 == target)
res18: Int = 2

Counting the occurrences of a single digit in a number sequence.
def countK(n:Int, k:Int):Int = {
assert(k >= 0 && k <= 9)
1.to(n).mkString.count(_ == '0' + k)
}
If you really only want to modify countKDigit() to a more functional design, there's always recursion.
def countKDigit(n:Int, k:Int, acc: Int = 0):Int =
if (n == 0) acc
else countKDigit(n/10, k, if (n%10 == k) acc+1 else acc)

Related

Product of Array Except Self with tail recursion

I would like to know if below function can be rewritten using tail recursion and using recursion in this case is any helpful in terms of time/space complexity?, below is what i have tried without tail recursion
input:[4,2,4,6]
output:[48,96,48,32]
def productExceptSelf(nums: Array[Int]): Array[Int]={
val len = nums.length
val output_arr= new Array[Int](len)
output_arr(0)=1
for(i<- 1 to len-1)
{
output_arr(i) = nums(i-1) * output_arr(i-1)
}
var R =1
var j=len-1
while(j>=0)
{
output_arr(j) = output_arr(j)* R
R = R * nums(j)
j-=1
}
output_arr
}
Solution without using division.
def productExceptSelf(nums: Array[Int]): Array[Int] =
Array.fill(nums.length)(nums)
.zipWithIndex
.map{case (ns,x) => ns.patch(x,Seq(),1).product}
Not sure how would you expect to write this using tail-recursion, but the simplest way would be something like this:
PS: I am using ArraySeq which is an immutable array (that was introduced on 2.13), feel free to keep using normal arrays.
def productExceptSelf(nums: ArraySeq[Int]): ArraySeq[Int] = {
val totalProduct = nums.product
nums.map(x => totalProduct / x)
}
Solution without using division.
def productExceptSelf(nums: ArraySew[Int]) : ArraySeq[Int] =
ArraySeq.tabulate(nums.length) { i =>
nums.foldLeft(1 -> 0) { case ((acc, j), x) =>
val newAcc = if (i == j) acc else acc * x
newAcc -> (j + 1)
}._1
}
Try this. It keeps track of the index and multiplies if the index of the current element isn't the same. However, it's not very idiomatic.
def productExceptSelf(nums: Array[Int]): Array[Int] =
productExceptSelf(nums, Array.fill(nums.size)(1), 0).toArray
def productExceptSelf(orig: Array[Int], res: Array[Int], i: Int): Array[Int] =
if (i == orig.size) res
else productExceptSelf(
orig,
res.zipWithIndex.map {
case (n, j) => if (j == i) n else n * orig(i)
},
i + 1
)
I like this one better:
def productExceptSelf(nums: Array[Int]): Array[Int] =
nums.indices.map {
i => nums.slice(0, i).product * nums.slice(i + 1, nums.size).product
}.toArray
You might prefer a view to do it lazily
def productExceptSelf(nums: Array[Int]) =
nums.indices.map {
i => nums.view.slice(0, i).product * nums.view.slice(i + 1, nums.size).product
}
# Starting from the right and recursing left, compute the right side product
# and pass it down. Upon reaching the left, compute the left side product,
# update the array, and bubble that value up.
def prod_not_self(arr, i, right):
if (i == 0):
left = 1
else:
left = arr[i-1] * prod_not_self(arr, i - 1, right * arr[i])
arr[i] = right * left
return left
if (__name__ == '__main__'):
arr = [1, 2, 3, 4]
print(arr)
prod_not_self(arr, len(arr) - 1, 1)
print(arr)
print()
arr = [-1, 1, 0, -3, 3]
print(arr)
prod_not_self(arr, len(arr) - 1, 1)
print(arr)

scala: sum of squares of every even-positioned digits starting from 2nd position

I can use the following code to sum up squares of every digits. how could it be changed to only do it for even-positioned digits starting from 2nd position ?
var sum:Int = num.toString.map{ _.asDigit }.map(x => x*x).sum
If you want every even-positioned value, one way of doing that is with grouped and head.
To start at the second digit, take the tail of the list before grouping.
val sum = num.toString.map{ _.asDigit }.tail.grouped(2).map(_.head).map(x => x*x).sum
See it working here.
A possible solution to keep only even-positioned is to use zipWithIndex:
// Your initial code
def squareDigitsSummation(n: Int): Int =
n.toString.map{ _.asDigit }.map(x => x*x).sum
def squareEvenPositionedDigitsSummation(n: Int): Int =
n.toString.view.
tail.
map(_.asDigit).
zipWithIndex.
collect { case (n, i) if i % 2 != 0 => n * n }.
sum
assert(squareDigitsSummation(1234) == 30)
assert(squareEvenPositionedDigitsSummation(1234) == 9)
Please note that zipWithIndex is 1-based, so I had to somewhat counter-intuitively negate the filter predicate to keep odd-indexed pairs (I assumed you wanted to have 0-based indexes, given you explicitly mentioned you didn't want the 0th element to be kept).
Sum of squares of even digits from the right - tail recursion
def sumOfEvenDigitsTailRecursion(num: Int): Int = {
#tailrec def impl(num: Int, idx: Int, acc: Int): Int = {
if (num == 0) acc
else if (idx % 2 == 0) impl(num / 10, idx + 1, acc + ((num % 10) * (num % 10)))
else impl(num / 10, idx + 1, acc)
}
impl(num, 1, 0)
}
assert(sumOfEvenDigitsTailRecursion(123456) == 5*5 + 3*3 +1*1)
Sum of squares of even digits from the left - tail recursion
def sumOfEvenDigitsTailRecursion(num: Int): Int = {
#tailrec def impl(num: Int, idx: Int, acc: Int, length: Int = -1): Int = {
if (length % 2 == 0) {
impl(num / 10, idx, acc + ((num % 10) * (num % 10)))
} else {
if (num == 0) acc
else if (idx % 2 == 0) impl(num / 10, idx + 1, acc + ((num % 10) * (num % 10)))
else impl(num / 10, idx + 1, acc)
}
}
impl(num, 1, 0, (Math.log10(num) + 1).toInt)
}
assert(sumOfEvenDigitsTailRecursion(123456) == 2*2 + 4*4 + 6*6)
Sum of squares of even digits from the left - iterators
def sumOfEvenDigitsIterators(num: Int): Int =
num
.toString
.iterator
.map(_.asDigit)
.grouped(2)
.collect{ case ArraySeq(_, b) => b }
.map(x => x * x)
.sum
Benchmark: sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 bench.So59627557"
#State(Scope.Benchmark)
#BenchmarkMode(Array(Mode.Throughput))
class So59627557 {
def _sumOfEvenDigitsIterators(num: Int): Int =
num
.toString
.iterator
.map(_.asDigit)
.grouped(2)
.collect{ case ArraySeq(_, b) => b }
.map(x => x * x)
.sum
def _sumOfEvenDigitsTailRecursion(num: Int): Int = {
#tailrec def impl(num: Int, idx: Int, acc: Int, length: Int = -1): Int = {
if (length % 2 == 0) {
impl(num / 10, idx, acc + ((num % 10) * (num % 10)))
} else {
if (num == 0) acc
else if (idx % 2 == 0) impl(num / 10, idx + 1, acc + ((num % 10) * (num % 10)))
else impl(num / 10, idx + 1, acc)
}
}
impl(num, 1, 0, (Math.log10(num) + 1).toInt)
}
val num: Int = (math.random * 100000000).toInt
#Benchmark def sumOfEvenDigitsIterators: Int = _sumOfEvenDigitsIterators(num)
#Benchmark def sumOfEvenDigitsTailRecursion: Int = _sumOfEvenDigitsTailRecursion(num)
}
results
[info] Benchmark Mode Cnt Score Error Units
[info] So59627557.sumOfEvenDigitsIterators thrpt 20 2033487.342 ± 187156.764 ops/s
[info] So59627557.sumOfEvenDigitsTailRecursion thrpt 20 27431255.610 ± 835429.506 ops/s
Tail-recursive solution seems to have more than 10x throughput than iterators-based solution.
Why not use regex?
val num = 24874
var sum: Int = "\\d{2}".r
.findAllIn(num.toString)
.map(_.last.asDigit)
.map(v => v * v)
.sum

MaxSubArray using dynamic style

I have been trying to create code that counts the max substring from array and returns sum and starting and ending point. I have some error in there however that I cannot spot because for example when I use Array(-2, 1, -3, 4, -1, 2, 1, -5, 4) as source I get return (7, 5,8). However, the correct answer should be (6, 3, 6). Code below.
def solve(a: Array[Int]): (Int, Int, Int) = {
require(a.nonEmpty)
val n = a.length
val temp = Array.fill(n)(0, 0, 0)
var max = (0,0,0) // sum, start, end
for (i <- 0 to n-1) {
temp(i) = (a(i), i, i)
}
for (i <- 0 to n-1) {
for (j <- 0 to i) {
if (a(i) > a(j) && temp(i)._1 < temp (j)._1 + a(i)) {
temp(i) = (temp(j)._1 + a(i), j, i)
}
}
}
for (i <- 0 to n-1){
if (max._1 < temp(i)._1){
max = temp(i)
}
}
return max
}
How about a more Scala/functional approach?
def solve(a: Array[Int]): (Int, Int, Int) = {
a.tails.zipWithIndex.flatMap{
case (arr, ti) =>
arr.inits.map{
tail => (tail.sum, ti, ti + tail.length -1)
}
}.maxBy(_._1)
}
solve (Array(-2, 1, -3, 4, -1, 2, 1, -5, 4)) => res7: (Int, Int, Int) = (6,3,6)

Three Sum to N in Scala

Is there a better way than this example to find three numbers from a list that sum to zero in scala? Right now, I feel like my functional way may not be the most efficient and it contains duplicate tuples. What is the most efficient way to get rid of duplicate tuples in my current example?
def secondThreeSum(nums:List[Int], n:Int):List[(Int,Int,Int)] = {
val sums = nums.combinations(2).map(combo => combo(0) + combo(1) -> (combo(0), combo(1))).toList.toMap
nums.flatMap { num =>
val tmp = n - num
if(sums.contains(tmp) && sums(tmp)._1 != num && sums(tmp)._2 != num) Some((num, sums(tmp)._1, sums(tmp)._2)) else None
}
}
This is pretty simple, and doesn't repeat any tuples:
def f(nums: List[Int], n: Int): List[(Int, Int, Int)] = {
for {
(a, i) <- nums.zipWithIndex;
(b, j) <- nums.zipWithIndex.drop(i + 1)
c <- nums.drop(j + 1)
if n == a + b + c
} yield (a, b, c)
}
Use .combinations(3) to generate all distinct possible triplets of your start list, then keep only those that sum up to n :
scala> def secondThreeSum(nums:List[Int], n:Int):List[(Int,Int,Int)] = {
nums.combinations(3)
.collect { case List(a,b,c) if (a+b+c) == n => (a,b,c) }
.toList
}
secondThreeSum: (nums: List[Int], n: Int)List[(Int, Int, Int)]
scala> secondThreeSum(List(1,2,3,-5,2), 0)
res3: List[(Int, Int, Int)] = List((2,3,-5))
scala> secondThreeSum(List(1,2,3,-5,2), -1)
res4: List[(Int, Int, Int)] = List((1,3,-5), (2,2,-5))
Here is a solution that's O(n^2*log(n)). So it's quite a lot faster for large lists.
Also it uses lower level language features to increase the speed even further.
def f(nums: List[Int], n: Int): List[(Int, Int, Int)] = {
val result = scala.collection.mutable.ArrayBuffer.empty[(Int, Int, Int)]
val array = nums.toArray
val mapValueToMaxIndex = scala.collection.mutable.Map.empty[Int, Int]
nums.zipWithIndex.foreach {
case (n, i) => mapValueToMaxIndex += (n -> math.max(i, (mapValueToMaxIndex.getOrElse(n, i))))
}
val size = array.size
var i = 0
while(i < size) {
val a = array(i)
var j = i+1
while(j < size) {
val b = array(j)
val c = n - b - a
mapValueToMaxIndex.get(c).foreach { maxIndex =>
if(maxIndex > j) result += ((a, b, c))
}
j += 1
}
i += 1
}
result.toList
}

Pattern match for Integer range

The purpose of my function is to add 5 to an integer as long as that integer is greater than 0 and less than or equal to 7. I try:
val add5Partial : PartialFunction[Int, Int] = {
case d if (0 < d <= 7) => d + 5;
}
I get:
<console>:8: error: type mismatch;
found : Int(7)
required: Boolean
case d if (0 < d <= 7) => d + 5;
Any tips?
Scala do not support such syntax out of the box, so you have to write:
val partial : Int => Int = {
case d if (d > 0) && (d <= 7) => d + 5;
}
Alternatively you could do:
val partial : Int => Int = {
case d if 1 to 7 contains d => d + 5;
}
You can't do this in a single comparison. You need to use:
(d > 0) && (d <= 7)
As you have done it, it will evaluate one comparison to a Boolean and then fail to use this as an int in the second comparison.
You can do any of the following:
val f = (n: Int) ⇒ if (n > 0 && n <= 7) n + 5 else n
// or ...
def f(n: Int) = if (n > 0 && n <= 7) n + 5 else n
// or ...
def f(n: Int): Int = n match {
case n if n > 0 && n <= 7 ⇒ n + 5
case _ ⇒ n
}
// or (to speak of ... the comment by #om-nom-nom)
def f(n: Int) = n match {
case n if 0 to 7 contains n ⇒ n + 5
case _ ⇒ n
}