Алгоритм смешивания - PullRequest
2 голосов
/ 27 июня 2010

У меня есть класс, который расширяет Iterator и моделирует сложный алгоритм (MyAlgorithm1).Таким образом, алгоритм может продвигаться шаг за шагом через метод Next.

class MyAlgorithm1(val c:Set) extends Iterator[Step] {
   override def next():Step {
       /* ... */
   }
   /* ... */
}

Теперь я хочу применить другой алгоритм (MyAlgorithm2) на каждом проходе первого алгоритма.Итерации алгоритма 1 и 2 должны быть вставлены

class MyAlgorithm2(val c:Set) { /* ... */ }

Как я могу сделать это наилучшим образом?Возможно, с некоторой чертой?

ОБНОВЛЕНИЕ:

MyAlgorithm2 получает набор и преобразует его.MyAlgorithm1 тоже, но это более сложный процесс, и он запускается шаг за шагом.Идея состоит в том, чтобы выполнить один шаг MyAlgoirthm1, а затем запустить MyAlgorithm2.Следующий шаг такой же.Действительно, MyAlgorithm2 упрощает набор и может быть полезен для упрощения работы MyAlgorithm1.

Ответы [ 4 ]

4 голосов
/ 28 июня 2010

Как описано, проблему можно решить с помощью наследования или черты.Например:

class MyAlgorithm1(val c:Set) extends Iterator[Step] {
  protected var current = Step(c)
  override def next():Step = {
    current = process(current)
    current 
  }
  override def hasNext: Boolean = !current.set.isEmpty
  private def process(s: Step): Step = s
}

class MyAlgorithm2(c: Set) extends MyAlgorithm1(c) {
  override def next(): Step = {
    super.next()
    current = process(current)
    current
  }
  private def process(s: Step): Step = s
}

С чертами вы могли бы что-то сделать с abstract override, но сконструировать его так, чтобы результат упрощения был загружен в первый алгоритм, было сложнее.Позвольте мне предложить вам неправильно решить проблему.

Вместо создания класса для алгоритма, расширяющего итератор, вы можете определить свой алгоритм следующим образом:

class MyAlgorithm1 extends Function1[Step, Step] {
  def apply(s: Step): Step = s
}

class MyAlgorithm2 extends Function1[Step, Step] {
  def apply(s: Step): Step = s
}

Тогда итератор может быть определен гораздо проще:

Iterator.iterate(Step(set))(MyAlgorithm1 andThen MyAlgorithm2).takeWhile(_.set.nonEmpty)
2 голосов
/ 28 июня 2010

Расширение Iterator, вероятно, требует больше работы, чем нужно на самом деле. Давайте немного откатимся.

У вас есть объект с состоянием типа MyAlgorithm1

val alg1 = new MyAlgorithm1(args)

Теперь вы хотите повторно вызывать некоторую функцию, которая изменит его состояние и вернет какое-то значение. Это лучше всего моделируется не тем, чтобы ваш объект реализовывал Iterator, а скорее созданием нового объекта, который обрабатывает итерацию. Вероятно, самый простой в стандартной библиотеке Scala - Stream. Вот объект, который создает поток результатов из вашего алгоритма.

val alg1Stream:Stream[Step] = Stream.continually(alg1.next())

Теперь, если вы хотите многократно получать результаты из этого потока, это будет так же просто, как

for(step<-alg1Stream){
   // do something
}

или эквивалентно

alg1Stream.forEach{
    //do something
}

Теперь предположим, что мы также инкапсулировали myAlgorithm2 в виде потока

val alg2=new MyAlgorithm2(args)
val alg2Stream:Stream[Step] = Stream.continually(alg2.next())

Тогда нам просто нужен какой-то способ чередования потоков, и тогда мы могли бы сказать

for(step<-interleave(alg1Stream, algStream2)){
   // do something
}

К сожалению, быстрый просмотр стандартной библиотеки не обнаруживает функции чередования потоков. Достаточно легко написать один

def interleave[A](stream1:Stream[A], stream2:Stream[A]):Stream[A] ={
    var str1 = stream1
    var str2 = stream2
    var streamToUse = 1
    Stream.continually{
        if(streamToUse == 1){
           streamToUse = 2
           val out = str1.head
           str1 = str1.tail
           out
        }else{
           streamToUse = 1
           val out = str2.head
           str2 = str1.tail
           out
        }
    }
}

Это создает поток, который периодически чередуется между двумя потоками, выбирая следующий результат из соответствующего и затем устанавливая его состояние для следующей выборки. Обратите внимание, что это чередование работает только для бесконечных потоков, и нам понадобится более умный для обработки потоков, которые могут закончиться, но для проблемы это нормально.

1 голос
/ 28 июня 2010

Кажется, что вы можете вместо этого использовать фолд или карту, в зависимости от того, что именно вы хотите сделать. Это обычный шаблон в функциональном программировании: вы генерируете список / последовательность / поток чего-либо, а затем запускаете функцию для каждого элемента. Если вы хотите запустить две функции для каждого элемента, вы можете либо составить функции, либо запустить другую карту.

1 голос
/ 27 июня 2010

У меня есть класс, который расширяет Iterator и моделирует сложный алгоритм (MyAlgorithm1).

Что ж, остановимся на мгновение.Алгоритм не является итератором, поэтому расширять Iterator.

не имеет смысла.
...