Scala, сделай мой цикл более функциональным - PullRequest
5 голосов
/ 27 декабря 2010

Я пытаюсь уменьшить степень написания Scala (2.8) как Java. Вот упрощение проблемы, с которой я столкнулся. Можете ли вы предложить улучшения для моих решений, которые являются "более функциональными"?

Преобразование карты

val inputMap = mutable.LinkedHashMap(1->'a',2->'a',3->'b',4->'z',5->'c')

, отбрасывая любые записи со значением 'z' и индексируя символы по мере их появления

Первая попытка

var outputMap = new mutable.HashMap[Char,Int]()
var counter = 0
for(kvp <- inputMap){
  val character = kvp._2
  if(character !='z' && !outputMap.contains(character)){
    outputMap += (character -> counter)
    counter += 1
  }
}

Вторая попытка (не намного лучше, но использует неизменную карту и 'foreach')

var outputMap = new immutable.HashMap[Char,Int]()
var counter = 0
inputMap.foreach{
  case(number,character) => {
    if(character !='z' && !outputMap.contains(character)){
      outputMap2 += (character -> counter)
      counter += 1
    }
  }
}

Ответы [ 6 ]

11 голосов
/ 27 декабря 2010

Хорошее решение:

inputMap.toList.filter(_._2 != 'z').map(_._2).distinct.zipWithIndex.toMap
9 голосов
/ 27 декабря 2010

Я считаю, что это решение немного проще, чем Арджана :

inputMap.values.filter(_ != 'z').toSeq.distinct.zipWithIndex.toMap

Отдельные шаги:

inputMap.values       // Iterable[Char]   = MapLike(a, a, b, z, c)
   .filter(_ != 'z')  // Iterable[Char]   = List(a, a, b, c)
   .toSeq.distinct    // Seq[Char]        = List(a, b, c)
   .zipWithIndex      // Seq[(Char, Int)] = List((a,0), (b,1), (c,2))
   .toMap             // Map[Char, Int]   = Map((a,0), (b,1), (c,2))

Обратите внимание, что ваша проблема по своей сути не связана скарта в качестве ввода, так как вы просто отбрасываете ключи.Если бы я кодировал это, я бы написал такую ​​функцию, как

def buildIndex[T](s: Seq[T]): Map[T, Int] = s.distinct.zipWithIndex.toMap

, и вызвал бы ее как

buildIndex(inputMap.values.filter(_ != 'z').toSeq)
5 голосов
/ 27 декабря 2010

Во-первых, если вы делаете это функционально, вы должны использовать неизменяемую карту.

Затем, чтобы избавиться от чего-либо, вы используете метод filter:

inputMap.filter(_._2 != 'z')

и, наконец, для переназначения вы можете просто использовать значения (но как набор) с zipWithIndex, который будет отсчитываться от нуля, а затем преобразовываться обратно в карту:

inputMap.filter(_._2 != 'z').values.toSet.zipWithIndex.toMap

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

Редактировать: есть лучшее решение в аналогичномвена;увидеть Арджана.Предположение (*) неверно, так как это был LinkedHashMap.Поэтому вам нужно сохранить порядок, как это делает решение Арджана.

3 голосов
/ 27 декабря 2010

Я хотел бы создать такой "конвейер", как этот, но он имеет много операций и может быть сокращен.Эти два List.map могут быть объединены в одно, но я думаю, у вас есть общее представление.

inputMap
.toList // List((5,c), (1,a), (2,a), (3,b), (4,z))
.sorted // List((1,a), (2,a), (3,b), (4,z), (5,c))
.filterNot((x) => {x._2 == 'z'}) // List((1,a), (2,a), (3,b), (5,c))
.map(_._2) // List(a, a, b, c)
.zipWithIndex // List((a,0), (a,1), (b,2), (c,3))
.map((x)=>{(x._2+1 -> x._1)}) // List((1,a), (2,a), (3,b), (4,c))
.toMap // Map((1,a), (2,a), (3,b), (4,c))

выполнение этих операций над списками сохраняет порядок элементов.

2 голосов
/ 27 декабря 2010

РЕДАКТИРОВАТЬ: я неправильно прочитал вопрос OP - думал, что вы хотите кодирование длины выполнения.Вот мой взгляд на ваш актуальный вопрос:

val values = inputMap.values.filterNot(_ == 'z').toSet.zipWithIndex.toMap

РЕДАКТИРОВАТЬ 2: Как отмечено в комментариях, используйте toSeq.distinct или аналогичный, если важно сохранить порядок.

val values = inputMap.values.filterNot(_ == 'z').toSeq.distinct.zipWithIndex.toMap
0 голосов
/ 27 декабря 2010

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

Я думаю, что это связано с картамиизменяемые структуры данных по своей природе.Учтите, что при построении списка базовая структура списка не изменяется при добавлении нового элемента и, если список истинный, то добавление является операцией с постоянной O (1).Принимая во внимание, что для карты внутренняя структура карты может значительно измениться при добавлении нового элемента, т.е.когда коэффициент загрузки становится слишком высоким и алгоритм добавления изменяет размер карты.Таким образом, функциональный язык не может просто создать серию значений и вставить их в карту, поскольку это происходит из-за возможных побочных эффектов от введения новой пары ключ / значение.

Тем не менее, я до сих пордумаю, что должна быть лучшая поддержка для фильтрации, картографирования и сворачивания / сокращения карт.Поскольку мы начинаем с карты, мы знаем максимальный размер карты, и ее легко создать.

Если вы хотите освоить функциональное программирование, я бы порекомендовалочистить от карт для начала.Придерживайтесь того, для чего созданы функциональные языки - манипулирование списками.

...