Unexpected behaviour for loop with "until" - scala

Recently, I've started learning Scala for school project. The assignment says that we have to delete elements of an unsorted array, without leaving "holes" in the array. I've made the following code:
private var _list = new Array[NAW](20)
private var _highestIndex = 0;
def removeAllbyName(name : String): Unit = {
for(i <- 0 until _highestIndex){
if(_list(i).name == name)
deleteByIndex(i)
}
}
def deleteByIndex(i : Int) : Unit = {
if(i != -1){
for(x <- i until _highestIndex){
_list(x) = _list(x + 1)
}
_highestIndex -= 1
}
}
I filled the array with 10 instances, the highestIndex will be 10 and after the first removal 9. But the for-loop still keeps counting to 9, which results in a null-pointer exception, even though we use "until". The debugger does recognise that _highestIndex is 9. Can somebody please explain why this is happening?
Thanks in advance!

0 until _highestIndex only gets evaluated once.
for (i <- 0 until _highestIndex) {
if (_list(i).name == name)
deleteByIndex(i)
}
Is equivalent to:
val range = 0 until _highestIndex
for (i <- range) {
if (_list(i).name == name)
deleteByIndex(i)
}
You can see that the range is determined only once.
You could try using a while loop instead, or a guard in your if statement.

Change your for-loop inside deleteByIndex like this to avoid exception
for(
x <- i until highestIndex
if (x + 1) < highestIndex
)
list(x) = list(x + 1)

Related

How to speed up this Scala solution for an easy Leetcode problem?

I solved easy Leetcode problem Ransom Note in Scala like this :
def canConstruct(ransomNote: String, magazine: String): Boolean = {
val magazineChars = magazine.toSeq.groupBy(identity).mapValues(_.size)
val ransomChars = ransomNote.toSeq.groupBy(identity).mapValues(_.size)
ransomChars.forall { case (c, num) => magazineChars.getOrElse(c, 0) >= num }
}
This solution is Ok but slower than other accepted solutions in Scala.
Now I wonder how to speed it up. How would you suggest optimize this solution ?
For performance purpose, you should use low level data structure (primitive type instead of object type, array of primitive type instead of List, Map, i.e.), and low level syntax (while instead of foreach loop, i.e.)
Here is my solution, which beats 90% ~ 100% (it's random), you can speed up it by replace foreach to while loop and replace forall to while loop too, but it's too tedious:
a slightly optimized version of the above solution:
def canConstruct(ransomNote: String, magazine: String): Boolean = {
if (magazine.length < ransomNote.length) {
false // if the magazine has fewer letters than the ransom note, then we definitely can't make the note
} else {
var i = 0
val counts = Array.ofDim[Int](26)
while (i < magazine.length) {
counts(magazine(i) - 'a') += 1
if (i < ransomNote.length) counts(ransomNote(i) - 'a') -= 1 // avoid the need for another loop for the ransom note letters
i += 1
}
var c = 0;
while (c < counts.length) {
if (counts(c) < 0) {
return false
}
c += 1
}
true
}
}
with the following results (after a few runs):

Apple postion in snake game scala

I am trying in my snake game to find the number of free spots in the grid then call randomGen.randomInt(nrFreeSpots). Then place the apple at the position corresponding to the number I got. I get an error then when i call getcelltype(i,j) too many arguments, Although the p point is int x and int y, I don't know how to fix it?
def getCellType(p : Point): CellType = {
for(i <- bodySize until 0 by -1){
if(p == snakeTail(i)) return SnakeBody()
}
if(p == snakeTail(0)) return SnakeHead(headDirection)
else if(p == applePosition) return Apple()
Empty()
}
def defineApplePosition(): Point = {
val freeCells = new Array[Point](gridDims.height * gridDims.width)
var counter: Int = 0
for(j <- 0 until gridDims.width; i <- 0 until gridDims.height){
if(getCellType(i,j) == Empty()){
freeCells(counter) = Point(i,j)
counter += 1
}
}
if(counter < 1) return null
freeCells(random.randomInt(counter))
}
getCellType takes one argument, p: Point. You've attempted to call this function with two Ints. You need to create a Point out of the two Ints and call getCellType with it. It should then compile

Scala Compilation Error : Value += is not member of Int

I am at a beginner level in Scala, I am trying the following code:
var i: Int = 0
for (i <- 0 to 10) {
if (i == 2) {
i += 1
}
println(i)
}
When I increment i the compiler says Value += is not member of Int.
You're declaring i twice, one at the outer scope and one in the inner. Your for comprehension looks at the inner i, which is a val, not a var, thus shadowing the external declaration.
Bind to a different name:
var counter: Int = 0
for (i <- 0 to 10) {
if (i == 2) {
counter += 1
}
println(counter)
}
Note you can do this without declaring an outer variable at all, using count:
val count = (0 to 10).count(i => i == 2)
println(count)
Edit:
From your comments, I understand you want to print all numbers other than 2. For that you need to negate your condition:
if (i != 2) {
counter += 1
}
Or if you just want to filter out 2:
(0 to 10).filter(_ != 2).foreach(println)
Edit 2:
From you comments, it looks like a while loop is better for what you're trying to achieve:
var i = 0
while (i < fruits.length) {
if (fruits(i) == "Banana") {
i += 1
println(i + " " + fruits(i))
} else if (fruits(i) == "Orange") {
i += 1
println(i + " " + fruits(i))
}
}
But this code will run into an infinite loop since you're only incrementing i if the conditions are met. Other than that, since your doing i + 1, you might be going out of bounds if the last element equals "Banana" or "Orange", so you might want to rethink what you're doing. You'll probably run into an
As mentioned in Yuval's answer, you're declaring i twice. Not only that, but the inner one is implicitly immutable, therefore making it impossible for you to mutate it (that's why you get a compilation error).
To achieve the same effect you get from mutation you can use a guard in the for comprehension, like this:
for (i <- 0 to 10 if i != 2) {
println(i)
}
This code can be seamlessly translated (and that's what the compiler actually does) to the following
(0 to 10).withFilter(i => i != 2).foreach(i => println(i))
The new error mentioned in the comment looks like:
scala> :pa
// Entering paste mode (ctrl-D to finish)
var i: Int = 0
for(i <- 0 to 10)
{
if (i == 2)
{
i += 1
}
println(i)
}
// Exiting paste mode, now interpreting.
<pastie>:19: error: value += is not a member of Int
Expression does not convert to assignment because receiver is not assignable.
i += 1
^
It should say i instead of receiver for the simple case, where the LHS is not a complex expression, but you get the idea.
Also, nobody mentioned this:
scala> :pa
// Entering paste mode (ctrl-D to finish)
object X {
var i: Int = 0
for(i <- 0 to 10)
{
if (i == 2)
{
this.i += 1
}
println(this.i)
}}
// Exiting paste mode, now interpreting.
defined object X
scala> X
0
0
1
1
1
1
1
1
1
1
1
res3: X.type = X$#1129829c

Accessing list of lists inside a for

I have the following bit of code in scala which is not working:
var tacTable : List[List[TACList]] = List(List())
def gen(p: Program) = {
for (i <- 0 to p.classes.length){
for (j <- 0 to p.classes(i).methods.length){
var tacInstr = new TACList()
tacTable(i)(j) = tacInstr //error: application does not take parameters
}
}
}
Apparently, it has to do with the fact that I'm using j to access the list and j is used in for...how can I solve this?
For convenience you can work with this other example which gives the same error:
var l : List[List[Int]] = List(List(1,2),List(3,4))
for (i <- 0 to l.length) {
for (j <- 0 to l.length) {
l(i)(j) = 8
}
}
Lists are immutable.
Try this instead:
val tacTable = p.classes.map { _.methods.map { _ =>
new TACList()
}
since I cannot comment on the initial post a sidenote here:
in a scala for-comprehension you can use multiple generators in a single for. so the nesting that you used is not necessary since you can use this:
for (i <- 0 to l.length; j <- 0 to l.length) {
// your code
}
furthermore, this does not seem to apply in your case but if you had a flat mapped result you should use the yield of the for comprehension instead of a mutation in the body

Changing between var and val in Scala leads to logical checks behaving differently

I was working with Project Euler Problem 4 (finding the largest palindrome formed from the product of two three-digit numbers), and produced the solution euler4_1, which uses a variable cand. The function runs as expected, iterating through all pairs (i, j) and only replacing cand with a bigger palindrome.
def euler4_1(): Int = {
var cand=0
for(i <- 100 until 999){
for(j <- 100 until i){
if (((i*j).toString.reverse == (i*j).toString)&&(i*j>cand)){
cand=i*j
println(cand)
}
}
}
return cand
}
/**
...
666666
678876
696696
749947
819918
824428
828828
861168
888888
906609
*/
However, initializing cand as a var instead of a val seems to mess things up - a number of palindromes which are smaller than the current best are also printed out.
def euler4_2(): Int = {
val cand=0
for(i <- 100 until 999){
for(j <- 100 until i){
if (((i*j).toString.reverse == (i*j).toString)&&(i*j>cand)){
val cand=i*j
println(cand)
}
}
}
return cand
}
/**
...
601106
650056
853358
141141
282282
119911
906609
514415
580085
*/
What's happening in the second code sample, labelled euler4_2?
[Comments on how to make the code more idiomatic in scala are also appreciated].
As n.m. pointed out in the comments, the problem is that you have two different val's named cand. If you want to write this in functional style, without any var's, use foldLeft and pass cand through each iteration:
(100 until 999).foldLeft(0) { (cand, i) =>
(100 until i).foldLeft(cand) { (cand, j) =>
val product = i * j
val productS = product.toString
if (productS.reverse == productS && product > cand) {
println(product)
product
} else cand
}
}
scala> (for (i <- 100 until 999 ;
| j <- 100 until i;
| ij = i * j;
| ijs = ij.toString if ijs == ijs.reverse
| ) yield ij
| ).max
res0: Int = 906609
would be the most obvious (to me) idiomatic functional-style implementation using comprehensions. Not sure about efficiency though.