how to use for-yield on this or other Scala functions to make it better?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

how to use for-yield on this or other Scala functions to make it better?

Felipe Gutierrez
Hello, I am java programmer and learning Scala. I did this program in Scala but it looks very Java yet. I would like some help to use some functions of Scala on it. Like for,yield or other tips that you can see.

This is a elevator that has a capacity X, weight limit Y. The building has M floors. THe array A represents the people weight and the array B represents the floors they want to go. The goal of the program is to count how many stops the elevator does. It is also necessary to count when the elevator comes back to the ground.


object ElevatorScala {

 
def main(args: Array[String]): Unit = {
   
// this should stop the elevator 5 times
   
// weight
    val A
= Array(60, 80, 40)
   
// target
    val B
= Array(2, 3, 5)
   
// floors
    val M
: Int = 5
   
// capacity
    val X
: Int = 2
   
// weight limit
    val Y
: Int = 200
    println
("solution ElevatorScala stops : " + solution(A, B, M, X, Y) + " times.")
 
}


 
def solution(A: Array[Int], B: Array[Int], M: Int, X: Int, Y: Int): Int = {


   
var sizeA: Int = 0
   
var stops: Int = 0
   
var floor: Int = 0
   
var total: Int = 0
   
var i: Int = 0


   
// for (i <- 0 until A.length) {
   
while (i < A.length) {
     
if ((total + 1) <= X && (sizeA + A(i)) <= Y) {
        total
= total + 1
        sizeA
+= A(i)


       
if (floor != B(i)) {
          stops
= stops + 1
       
}
        floor
= B(i)
     
} else {
       
// elevator goes down
        stops
= stops + 1
        total
= 0
        sizeA
= 0
        floor
= 0
        i
= i - 1
     
}
      println
("passenger " + i + " : " + A(i) + " floor: " + B(i) + " stops: " + stops)
      i
= i + 1
   
}
    stops
= stops + 1
   
return stops
 
}
}

Thanks,
Felipe

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: how to use for-yield on this or other Scala functions to make it better?

Vlad Patryshev
Felipe,

Yield in loops is used to do either map or flatmap (ok, or filter). That is, you have one stream or collection, and you produce another by writing yield in a loop. While-loop is not a collection-based loop, so yield hardly makes any sense there.
Of course switching from imperative Java style to functional Scala style takes time. I'd suggest to read some basic Scala books, and experimenting with projecteuler.net problems, not just jumping in to practical problems and quick solutions.

Thanks,
-Vlad

On Tue, Jan 24, 2017 at 12:21 PM, Felipe Gutierrez <[hidden email]> wrote:
Hello, I am java programmer and learning Scala. I did this program in Scala but it looks very Java yet. I would like some help to use some functions of Scala on it. Like for,yield or other tips that you can see.

This is a elevator that has a capacity X, weight limit Y. The building has M floors. THe array A represents the people weight and the array B represents the floors they want to go. The goal of the program is to count how many stops the elevator does. It is also necessary to count when the elevator comes back to the ground.


object ElevatorScala {

 
def main(args: Array[String]): Unit = {
   
// this should stop the elevator 5 times
   
// weight
    val A
= Array(60, 80, 40)
   
// target
    val B
= Array(2, 3, 5)
   
// floors
    val M
: Int = 5
   
// capacity
    val X
: Int = 2
   
// weight limit
    val Y
: Int = 200
    println
("solution ElevatorScala stops : " + solution(A, B, M, X, Y) + " times.")
 
}


 
def solution(A: Array[Int], B: Array[Int], M: Int, X: Int, Y: Int): Int = {


   
var sizeA: Int = 0
   
var stops: Int = 0
   
var floor: Int = 0
   
var total: Int = 0
   
var i: Int = 0


   
// for (i <- 0 until A.length) {
   
while (i < A.length) {
     
if ((total + 1) <= X && (sizeA + A(i)) <= Y) {
        total
= total + 1
        sizeA
+= A(i)


       
if (floor != B(i)) {
          stops
= stops + 1
       
}
        floor
= B(i)
     
} else {
       
// elevator goes down
        stops
= stops + 1
        total
= 0
        sizeA
= 0
        floor
= 0
        i
= i - 1
     
}
      println
("passenger " + i + " : " + A(i) + " floor: " + B(i) + " stops: " + stops)
      i
= i + 1
   
}
    stops
= stops + 1
   
return stops
 
}
}

Thanks,
Felipe

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: how to use for-yield on this or other Scala functions to make it better?

Lanny Ripple
In reply to this post by Felipe Gutierrez
So a first way to move to a less Java-y feel is to figure out how to remove vars.  Vars are supported in Scala and there are times they are a great optimization.  As a first cut though you want to avoid them and work to keep your state immutable.  (If state is immutable how do you update it?  By passing a changed state into a new function call.)

Looking at the code you supply you are using a while loop because you need the index to reference to different Arrays.  We'll start by modifying that data so you don't need an index.

// val AB = A.zipWith(B).toList
val AB
= List(60 -> 2, 80 -> 3, 40 -> 5)

Now we want to get rid of the vars.  We'll introduce a case class to track everything.

case class State(count: Int, weight: Int, floor: Int, stops: Int) {
 
import State._

 
def canAccept(moreWeight: Int): Boolean = count + 1 <= capacity && weight + moreWeight <= maxWeight

 
def accept(moreWeight: Int): State = this.copy(count = count + 1, weight = weight + moreWeight)

 
def toFloor(nextFloor: Int): State = {
   
if (nextFloor < 1 && nextFloor > topFloor) sys.error(s"Invalid floor: $nextFloor")

   
// Not ideal that floor 1 is magic this way but we'll go with it.
   
if (nextFloor == 1) State.empty.copy(stops = stops + 1)
   
else this.copy(floor = newFloor, stops = stop + 1)
 
}
}

object State {
 
final val topFloor = 5
 
final val capacity = 2
 
final val maxWeight = 200

  val empty
: State = State(0, 0, 1, 0)
}

At this point we've encapsulated state change into defined steps.  Now we'll make use of a higher-order function named unfold.  Unfold takes some initial state, and then runs code to advance that state while collecting intermediate results into a List.  You have full control over the state transition (meaning you don't have to advance the state if not desired.  Your i = i - 1 step.).

def unfold[S,A](init: S)(body: S => Option[(S,A)]): List[A] = {
 
@annotation.tailrec
 
def worker(state: S, acc: List[A]): List[A] = {
    body
(state) match {
     
case Some((newState, elt)) => worker(newState, acc :: elt)
     
case None => acc
   
}
 
}

  worker
(init, List.empty[A])
}

Unfold would be in a library somewhere (e.g., Scalaz or Cats).  With all that set up your program becomes

val steps =
  unfold
(AB -> State.empty) {
   
case (data, state) =>
      data match
{
       
// Return None so unfold knows to stop.
       
case Nil => Option.empty[((List[(Int,Int)], State),State)]

       
case (moreWeight, nextFloor) :: ts =>
         
if (state.canAccept(moreWeight)) {
            val step
= state.accept(moreWeight).toFloor(nextFloor)
           
Option( (ts -> step, step) )
         
}
         
else {
            val step
= state.toFloor(1)
           
// Use `data` so we don't advance passengers
           
Option( (data -> step, step) )
         
}
     
}
 
}

steps
.headOption.fold(0)(_.stops)


Now the question is, "Is this better?"  I think I can argue that even to a Java programmer that encapsulating the state change as I did is better.  You have operations that you can eyeball and see what will change when they are called.  The var solution you were using doesn't really indicate how the state will be changing unless you dig into the code.

The unfold on the other hand is quite an eyeful if you aren't used to it.  I know I think it's better because I'm used to it and it does exactly what I was looking for (stepping through state transitions and building up a list of results) and if I told my cow-orkers that, "I solved it with an unfold", then they would all know what I meant and could visualize the solution based on that knowledge.  So often times "better" really just means "used to doing it that way".

All that said once you learn the functional way you tend to throw rocks at other approaches.  :)

  Good luck!

On Tuesday, January 24, 2017 at 2:21:57 PM UTC-6, Felipe Gutierrez wrote:
Hello, I am java programmer and learning Scala. I did this program in Scala but it looks very Java yet. I would like some help to use some functions of Scala on it. Like for,yield or other tips that you can see.

This is a elevator that has a capacity X, weight limit Y. The building has M floors. THe array A represents the people weight and the array B represents the floors they want to go. The goal of the program is to count how many stops the elevator does. It is also necessary to count when the elevator comes back to the ground.


object ElevatorScala {

 
def main(args: Array[String]): Unit = {
   
// this should stop the elevator 5 times
   
// weight
    val A
= Array(60, 80, 40)
   
// target
    val B
= Array(2, 3, 5)
   
// floors
    val M
: Int = 5
   
// capacity
    val X
: Int = 2
   
// weight limit
    val Y
: Int = 200
    println
("solution ElevatorScala stops : " + solution(A, B, M, X, Y) + " times.")
 
}


 
def solution(A: Array[Int], B: Array[Int], M: Int, X: Int, Y: Int): Int = {


   
var sizeA: Int = 0
   
var stops: Int = 0
   
var floor: Int = 0
   
var total: Int = 0
   
var i: Int = 0


   
// for (i <- 0 until A.length) {
   
while (i < A.length) {
     
if ((total + 1) <= X && (sizeA + A(i)) <= Y) {
        total
= total + 1
        sizeA
+= A(i)


       
if (floor != B(i)) {
          stops
= stops + 1
       
}
        floor
= B(i)
     
} else {
       
// elevator goes down
        stops
= stops + 1
        total
= 0
        sizeA
= 0
        floor
= 0
        i
= i - 1
     
}
      println
("passenger " + i + " : " + A(i) + " floor: " + B(i) + " stops: " + stops)
      i
= i + 1
   
}
    stops
= stops + 1
   
return stops
 
}
}

Thanks,
Felipe

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: how to use for-yield on this or other Scala functions to make it better?

Lanny Ripple
Oops.  Saw this and meant to correct before posting.  In the unfold it should read

  case Some((newState, elt)) => worker(newState, elt :: acc)


On Wednesday, January 25, 2017 at 2:08:35 PM UTC-6, Lanny Ripple wrote:
So a first way to move to a less Java-y feel is to figure out how to remove vars.  Vars are supported in Scala and there are times they are a great optimization.  As a first cut though you want to avoid them and work to keep your state immutable.  (If state is immutable how do you update it?  By passing a changed state into a new function call.)

Looking at the code you supply you are using a while loop because you need the index to reference to different Arrays.  We'll start by modifying that data so you don't need an index.

// val AB = A.zipWith(B).toList
val AB
= List(60 -> 2, 80 -> 3, 40 -> 5)

Now we want to get rid of the vars.  We'll introduce a case class to track everything.

case class State(count: Int, weight: Int, floor: Int, stops: Int) {
 
import State._

 
def canAccept(moreWeight: Int): Boolean = count + 1 <= capacity && weight + moreWeight <= maxWeight

 
def accept(moreWeight: Int): State = this.copy(count = count + 1, weight = weight + moreWeight)

 
def toFloor(nextFloor: Int): State = {
   
if (nextFloor < 1 && nextFloor > topFloor) sys.error(s"Invalid floor: $nextFloor")

   
// Not ideal that floor 1 is magic this way but we'll go with it.
   
if (nextFloor == 1) State.empty.copy(stops = stops + 1)
   
else this.copy(floor = newFloor, stops = stop + 1)
 
}
}

object State {
 
final val topFloor = 5
 
final val capacity = 2
 
final val maxWeight = 200

  val empty
: State = State(0, 0, 1, 0)
}

At this point we've encapsulated state change into defined steps.  Now we'll make use of a higher-order function named unfold.  Unfold takes some initial state, and then runs code to advance that state while collecting intermediate results into a List.  You have full control over the state transition (meaning you don't have to advance the state if not desired.  Your i = i - 1 step.).

def unfold[S,A](init: S)(body: S => Option[(S,A)]): List[A] = {
 
@annotation.tailrec
 
def worker(state: S, acc: List[A]): List[A] = {
    body
(state) match {
     
case Some((newState, elt)) => worker(newState, acc :: elt)
     
case None => acc
   
}
 
}

  worker
(init, List.empty[A])
}

Unfold would be in a library somewhere (e.g., Scalaz or Cats).  With all that set up your program becomes

val steps =
  unfold
(AB -> State.empty) {
   
case (data, state) =>
      data match
{
       
// Return None so unfold knows to stop.
       
case Nil => Option.empty[((List[(Int,Int)], State),State)]

       
case (moreWeight, nextFloor) :: ts =>
         
if (state.canAccept(moreWeight)) {
            val step
= state.accept(moreWeight).toFloor(nextFloor)
           
Option( (ts -> step, step) )
         
}
         
else {
            val step
= state.toFloor(1)
           
// Use `data` so we don't advance passengers
           
Option( (data -> step, step) )
         
}
     
}
 
}

steps
.headOption.fold(0)(_.stops)


Now the question is, "Is this better?"  I think I can argue that even to a Java programmer that encapsulating the state change as I did is better.  You have operations that you can eyeball and see what will change when they are called.  The var solution you were using doesn't really indicate how the state will be changing unless you dig into the code.

The unfold on the other hand is quite an eyeful if you aren't used to it.  I know I think it's better because I'm used to it and it does exactly what I was looking for (stepping through state transitions and building up a list of results) and if I told my cow-orkers that, "I solved it with an unfold", then they would all know what I meant and could visualize the solution based on that knowledge.  So often times "better" really just means "used to doing it that way".

All that said once you learn the functional way you tend to throw rocks at other approaches.  :)

  Good luck!

On Tuesday, January 24, 2017 at 2:21:57 PM UTC-6, Felipe Gutierrez wrote:
Hello, I am java programmer and learning Scala. I did this program in Scala but it looks very Java yet. I would like some help to use some functions of Scala on it. Like for,yield or other tips that you can see.

This is a elevator that has a capacity X, weight limit Y. The building has M floors. THe array A represents the people weight and the array B represents the floors they want to go. The goal of the program is to count how many stops the elevator does. It is also necessary to count when the elevator comes back to the ground.


object ElevatorScala {

 
def main(args: Array[String]): Unit = {
   
// this should stop the elevator 5 times
   
// weight
    val A
= Array(60, 80, 40)
   
// target
    val B
= Array(2, 3, 5)
   
// floors
    val M
: Int = 5
   
// capacity
    val X
: Int = 2
   
// weight limit
    val Y
: Int = 200
    println
("solution ElevatorScala stops : " + solution(A, B, M, X, Y) + " times.")
 
}


 
def solution(A: Array[Int], B: Array[Int], M: Int, X: Int, Y: Int): Int = {


   
var sizeA: Int = 0
   
var stops: Int = 0
   
var floor: Int = 0
   
var total: Int = 0
   
var i: Int = 0


   
// for (i <- 0 until A.length) {
   
while (i < A.length) {
     
if ((total + 1) <= X && (sizeA + A(i)) <= Y) {
        total
= total + 1
        sizeA
+= A(i)


       
if (floor != B(i)) {
          stops
= stops + 1
       
}
        floor
= B(i)
     
} else {
       
// elevator goes down
        stops
= stops + 1
        total
= 0
        sizeA
= 0
        floor
= 0
        i
= i - 1
     
}
      println
("passenger " + i + " : " + A(i) + " floor: " + B(i) + " stops: " + stops)
      i
= i + 1
   
}
    stops
= stops + 1
   
return stops
 
}
}

Thanks,
Felipe

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.