2013-02-17 2 views
2

Я играю с некоторыми базовыми упражнениями по программированию, чтобы лучше изучить Scala, но я застрял пытаясь понять, почему мой код не будет вводить проверку.Я хочу, чтобы моя функция возвращала Stream [T], но я не могу понять, как это сделать. Check

Точка крепления - это функция possibilities. Я хочу функцию, которая возвращает поток, содержащий все возможные расположения чисел и математических операторов, с учетом списка чисел.

Я смущен, потому что изменение типа возвращаемого значения функции для чтения Stream[Object] проверяет корректность и возвращает результаты, которые кажутся потоками уравнений. Однако версия, приведенная ниже , не содержит тип проверки с типом возврата possibilites, установленным на Stream[Equation].

В качестве примечания стороны, я понимаю, что добавление opsMix с карточками не помещает Operation s в правильном порядке, но сначала я хотел бы решить эту часть проблемы. Думаю, я воспользуюсь flatMap или zipAll с flatten, чтобы выполнить эту часть.

Также - это не домашнее задание!

abstract class Operation 
case class Add() extends Operation 
case class Subtract() extends Operation 
case class Multiply() extends Operation 
case class Divide() extends Operation 
case class Num(val valu: Float) extends Operation 

type Equation = List[Operation] 

def calc(equa: Equation): Float = 
    equa match { 
    case Num(x) :: List() => x 
    case Num(x) :: y :: Num(z) :: xs => y match { 
     case Add() => calc(Num(x + z)::xs) 
     case Subtract() => calc(Num(x - z)::xs) 
     case Multiply() => calc(Num(x * z)::xs) 
     case Divide() => calc(Num(x/z)::xs) 
    } 
    case _ => 0 
    } 

// from http://stackoverflow.com/questions/1070859/listing-combinations-with-repetitions-in-scala 
def mycomb[T](n: Int, l: List[T]): List[List[T]] = 
    n match { 
    case 0 => List(List()) 
    case _ => for(el <- l; 
       sl <- mycomb(n-1, l dropWhile { _ != el })) 
       yield el :: sl 
} 
def comb[T](n: Int, l: List[T]): List[List[T]] = mycomb(n, l.removeDuplicates) 

val ops = List(Add, Subtract, Multiply, Divide) 
def possibilities(cards: List[Num]) : Stream[Equation] = 
    { for { 
     hand <- cards.permutations 
     opMix <- comb(cards.length-1, ops) 
    } yield hand ++ opMix 
    }.toStream 

// test value: 
val ppp = possibilities(List(Num(20), Num(3), Num(7), Num(100))) 

ответ

4

Проблема заключается в том, что вы объявили свои операции классы случай, как Add() и т.д., но в val ops вы используете только List(Add, ...). Если вы пытаетесь объявить ops с правильным типом:

val ops: List[Operation] = List(Add, Subtract, Multiply, Divide) 

вы будете видеть ошибки. (Вот почему это часто бывает полезно добавить типы самостоятельно, вместо того, чтобы полагаться на тип проверки - это помогает находить ошибки.)

Я предлагаю вам обновить иерархию классов, чтобы использовать case object для одноэлементных операций:

abstract class Operation 
    case object Add extends Operation 
    case object Subtract extends Operation 
    case object Multiply extends Operation 
    case object Divide extends Operation 
    case class Num(val valu: Float) extends Operation 

конечно, вы должны обновить свои модели, а также:

def calc(equa: Equation): Float = 
    equa match { 
     case Num(x) :: List() => x 
     case Num(x) :: y :: Num(z) :: xs => y match { 
     case Add => calc(Num(x + z)::xs) 
     case Subtract => calc(Num(x - z)::xs) 
     case Multiply => calc(Num(x * z)::xs) 
     case Divide => calc(Num(x/z)::xs) 
     } 
     case _ => 0 
    } 

Тогда possibilities работ, как ожидается, без каких-либо изменений.

В качестве альтернативы, вы можете держать классов так, как вы их, просто изменить ops к

val ops: List[Operation] = 
    List(Add(), Subtract(), Multiply(), Divide()) 

Update: Относительно перемежения, вы могли бы сделать что-то вроде:

def interleave[T](xs: List[T], ys: List[T], padX: T, padY: T): List[T] = 
    xs.zipAll(ys, padX, padY).flatMap(pair => List(pair._1, pair._2)) 

, но обратите внимание, что результат всегда будет иметь четное число элементов. Возможно, лучшим решением было бы реализовать interleave себя, что-то вроде:

def interleave[T](xs: List[T], ys: List[T]): List[T] = { 
    import collection.mutable._; 
    @annotation.tailrec 
    def f(xs: List[T], ys: List[T], r: Buffer[T]): Buffer[T] = 
    xs match { 
     // By swapping the arguments, we get interelaving: 
     case x :: xrest => f(ys, xrest, r += x); 
     case Nil   => r ++= ys; 
    } 
    return f(xs, ys, new ArrayBuffer[T]).toList; 
} 

Однако, я бы сказал, что даже лучшим решением было бы не смешивать операции и числа.Вместо этого, вы можете объявить специальный класс для хорошо сформированным выражения, сформированного из ваших символов, что-то вроде (непроверенный):

sealed abstract class Symbol 
sealed abstract class Operation extends Symbol 
case object Add Operation 
case object Subtract extends Operation 
case object Multiply extends Operation 
case object Divide extends Operation 
case class Num(val valu: Float) extends Symbol 

sealed abstract class Expression; 
case class App(val op: Operation, val arg1: Expression, val arg2: Expression) 
    extends Expression; 
case class Const(val n: Num) 
    extends Expression; 

и вместо создания перемежающегося списка, создать экземпляр Expression.

+0

Спасибо, хороший ответ. Ваши предложения полностью верны. Теперь, пытаясь использовать zipAll и сгладить, как я планировал, я столкнулся с другой ошибкой, связанной с типом ... Какова будет ваша первая идея поместить элементы 'card' и 'opMix' в порядке чередования? –

+0

@DustMason Я добавил некоторые идеи в ответ. –

+0

Очень полезно, спасибо! Ваши окончательные предложения больше похожи на «scala way» для меня, поскольку он решает проблему с четко определенными типизированными классами. –

Смежные вопросы