Scala - operowanie na pierwszych n elementach listy

Scala - operowanie na pierwszych n elementach listy
danek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 797
0

Hej
Zacząłem się uczyć wczoraj scali i napisałem funkcje do obliczania wartości wyrażenia zapisanego w odwróconej notacji polskiej:

Kopiuj

  def main(args: Array[String]): Unit = {
        println(eval(List("1","2","*","3","+")))
}
  def eval(t: List[String]): Int = {
    def eval(t: List[String], valueStack: List[Int]): Int = t match {
      case Nil => valueStack.head
      case "+" :: _ => eval(t.tail, (valueStack.head + valueStack.drop(1).head) :: valueStack.drop(2));
      case "-" :: _ => eval(t.tail, (valueStack.head - valueStack.drop(1).head) :: valueStack.drop(2));
      case "*" :: _ => eval(t.tail, (valueStack.head * valueStack.drop(1).head) :: valueStack.drop(2));
      case "*" :: _ => eval(t.tail, (valueStack.head / valueStack.drop(1).head) :: valueStack.drop(2));
      case n :: _ => eval(t.tail, n.toInt :: valueStack)
    }
    eval(t, Nil)
  }

Pytanie jak to zrobić 'ładniej'. Chodzi mi o ten kawałek gdzie dodaje dwa pierwsze elementy do siebie i robię z nich nową głowe listy. Czy też może jakąś sprytną strukturą klas to załatwić?

EDIT

Wymyśliłem takie rozwiązanie. Lepsze?

Kopiuj
  def eval(t: List[String]): Int = {
    def eval(t: List[String], valueStack: List[Int]): Int = t match {
      case Nil => valueStack.head
      case "+" :: _ => eval(t.tail, operateOnFirstDwo[Int](valueStack, _ + _))
      case "-" :: _ => eval(t.tail, operateOnFirstDwo[Int](valueStack, _ - _))
      case "*" :: _ => eval(t.tail, operateOnFirstDwo[Int](valueStack, _ * _))
      case "/" :: _ => eval(t.tail, operateOnFirstDwo[Int](valueStack, _ / _))
      case n :: _ => eval(t.tail, n.toInt :: valueStack)
    }
    eval(t, Nil)
  }

  def operateOnFirstDwo[T](list:List[T], f: (T,T)=>T):List[T] = {
    f(list.head, list.drop(1).head) :: list.drop(2)
  }

jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
1

zamiast

Kopiuj
case "+" :: _ => eval(t.tail, operateOnFirstDwo[Int](valueStack, _ + _))

możesz napisać

Kopiuj
 case "+" :: tail => eval(tail, operateOnFirstDwo[Int](valueStack, _ + _))

taka drobnostka

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
0

Działa Ci to? Przetestowałeś?

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
1

"Wydaje się działać" A to dobre!:)
Ok, to co Masz dla:

Kopiuj
'7 8 + 3 2 + /'
'1 2 + 3 4 + *'
"1 2 + 3 * 4 5 - 6 7 + * -"

Powinno być, odpowiednio: 3, 21, 22.

danek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 797
0

@lion137:
dzięki, masz rację. Powinno być

Kopiuj
  def operateOnFirstDwo[T](list:List[T], f: (T,T)=>T):List[T] = {
    f(list.drop(1).head,list.head) :: list.drop(2)
  }
Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Po co robić list.drop(x).head skoro można zrobić wprost list(x)?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
1

Nieco bardziej odpicowana wersja:

Kopiuj
import scala.annotation.tailrec
import scala.util.Try

object Evaluator {
  def main(args: Array[String]): Unit = {
    println(eval(List("1", "2", "*", "3", "+")))
  }

  private val ops = Map[String, (Int, Int) => Int](
    "+" -> (_ + _),
    "-" -> (_ - _),
    "*" -> (_ * _),
    "/" -> (_ / _),
  )

  def eval(tokens: List[String]): Int = {
    @tailrec
    def go(tokens: List[String], values: List[Int]): Int = {
      (tokens, values) match {
        case (Nil, List(result)) =>
          result
        case (op :: tokensTail, arg1 :: arg2 :: valuesTail) if ops.contains(op) =>
          val result = ops(op)(arg1, arg2)
          go(tokensTail, result :: valuesTail)
        case (n :: tokensTail, _) if Try(n.toInt).isSuccess =>
          go(tokensTail, n.toInt :: values)
        case _ =>
          sys.error(s"invalid state. tokens = $tokens, values = $values")
      }
    }
    go(tokens, Nil)
  }
}

Powinna niezawodnie raportować błędy zamiast się wykrzaczać. Zaraportuje błąd także w przypadku, gdy na końcu przetwarzania na stosie wartości będzie więcej niż jedna wartość (wynik powinien być jeden).

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
0

@Wibowit: Wydaje mi się, że w Scali już nie dawno nie trzeba dekoratora @tailrec

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Generalnie chyba nigdy nie trzeba było, ale jeśli ta adnotacja jest to kompilacja wywala się, gdy kompilator nie może zastosować optymalizacji.

A method annotation which verifies that the method will be compiled with tail call optimization.

If it is present, the compiler will issue an error if the method cannot be optimized into a loop.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.