Scala: избегайте var или изменяемого типа

У меня есть псевдокод, как показано ниже. ItemChurner.churn() — это абстрактный компонент, который генерирует объекты до x раз, где x неизвестно. :

def func: MyList = {

    var list: MyList = MyList()

    while(ItemChurner.canChurn) {
        list = new MyList(ItemChurner.churn(), list)
    }

    list
}

Есть ли способ избежать использования var?


person Mohitt    schedule 11.09.2016    source источник
comment
вы можете реализовать Iterator интерфейс, например val iterator = new Iterator {def hasNext = ItemChurner.canChurn; def next = ItemChurner.churn() , а затем iterator.toList   -  person dk14    schedule 11.09.2016
comment
Почему вы оборачиваете внутреннюю часть цикла while с помощью try-catch? Разве ItemChurner не должно возвращать false, если элементов больше нет?   -  person Yuval Itzchakov    schedule 11.09.2016
comment
@YuvalItzchakov: правильно. Исправил фрагмент.   -  person Mohitt    schedule 11.09.2016


Ответы (3)


Если canChurn работает как надо:

def func(churner: ItemChurner) = {
  val iterator = new Iterator {
    def hasNext = churner.canChurn
    def next = churner.churn()
  }
  iterator.toList
}

О версии (вопроса), которая содержала проверку перехваченных исключений для churn():

Если действительно ожидать каких-то исключений, то какой тогда смысл в canChurn?

В любом случае, если вы заботитесь об исключениях:

 Iterator.continually(Try(churner.churn)).takeWhile(_.isSuccess).map(_.get).toList

На самом деле это не очень точно, так как churn может выдать какое-то другое исключение, которое необходимо распространить, поэтому здесь помощники по исключениям):

def step = catching(classOf[NoMoreElementsException]) opt churner.churn()
Iterator.continually(step).takeWhile(_.nonEmpty).map(_.get).toList
person dk14    schedule 11.09.2016
comment
@dk12, да. Просто понял, что если у меня есть canChurn, то нет необходимости ловить исключения. Спасибо, что указали. Вопрос обновлен. - person Mohitt; 11.09.2016
comment
@ dk12 Я думаю, что Iterator.continually(if (churner.canChurn) Some(churner.churn()) else None).takeWhile(_.isEmpty).map(_.get).toList - лучший выбор, поскольку он опирается только на открытую часть возможностей churner. - person sarveshseri; 11.09.2016
comment
@SarveshKumarSingh это зависит. Перехват исключения внутри Try может быть медленнее, кроме того, код, который фактически проверяет наличие точного исключения, сигнализирующего об отсутствии элементов, менее элегантен (может потребоваться Помощники по исключениям) - person dk14; 11.09.2016

Если вы хотите использовать простую рекурсию и избегать var. Это общая стратегия, используемая в функциональном программировании.

Используйте Vector вместо List для эффективного append

def func[T](): List[T] = {

  @tailrec
  def helper(result: List[T]): List[T] = {
    if (ItemChurner.canChurn) helper(result ++ List(ItemChurner.churn))
    else result
  }

  helper(List.Empty[T])
}

Предположим, что ItemChurner.canChurn не создает никаких исключений. если он генерирует исключения, просто оберните его внутри Try

person pamu    schedule 11.09.2016
comment
Что касается эффективности, то List кажется не лучшим выбором, так как List ++ List имеет линейную сложность. Vector имеет фактически постоянное добавление - person dk14; 11.09.2016
comment
@ dk14, ты абсолютно прав. Благодарю. отредактировал ответ. - person pamu; 11.09.2016

Улучшив ответ pamu, вы можете сделать что-то в строках;

def func: MyList = {
  def process(cur: MyList): MyList = {
    if (ItemChurner.canChurn) process(new MyList(ItemChurner.churn(), cur))
    else cur
  }
  process(new MyList())
}
person Ashesh    schedule 11.09.2016