как сделать этот код функциональным? - PullRequest
4 голосов
/ 17 августа 2011

Я написал простой скрипт для преобразования имен идентификаторов в стиле c (например, invoice_number) в имена в стиле java (например, invoiceNumber).

val files = Vector("file1", "file2")
for (file <- files) {
  val in = io.Source.fromFile(file).mkString
  var out = ""
  var i = 0
  while (i < in.length) {
    val c = in(i)
    if (c == '_') {
      out += in(i + 1).toUpper
      i += 2
    } else {
      out += c
      i += 1
    }
  }
  val writer = new PrintWriter(file + "1")
  writer.write(out)
  writer.flush()
  writer.close()
}

Мне бы хотелосьзнать, как я могу сделать этот код функциональным.я не могу придумать какую-либо функцию более высокого порядка, чтобы заменить «приращение я на 2, еслиостальное приращение на 1 "логика. спасибо.

Ответы [ 6 ]

6 голосов
/ 17 августа 2011

Хорошо, вот мой способ сделать это.

val in = "identifier" :: "invoice_number" :: "some_other_stuff" :: Nil
val out = in map(identifier => {
  val words = identifier.split("_")
  val tail = words.tail.map(_.capitalize)
  (words.head /: tail)(_ + _)
})

println(in)
println(out)

Думаю, это в разумном функциональном стиле. Интересно, как гуру-скала решат эту проблему:)

4 голосов
/ 18 августа 2011

Другое решение;используя скользящее окно итератора:

  println(
    (" ".iterator ++ io.Source.fromFile(file)).sliding(2).map {
      s => if (s(0) == '_') s(1).toUpper else s(1)
    }.filter(_ != '_').mkString)
4 голосов
/ 17 августа 2011

Я прошу прощения за то, что не использовал Scala, но основная идея должна быть переведена, поэтому вот как я написал бы основную логику в Haskell:

capitalize :: Char -> String -> String
capitalize '_' (x:xs) = toUpper x:xs
capitalize x xs = x:xs

convertName :: String -> String
convertName = foldr capitalize ""

У нас есть две части: функция, которая в верхнем регистре первойсимвол строки, если ему дано подчеркивание или что-то еще, добавляет его к строке.Затем мы просто используем это в правой части последовательности входных символов с пустой строкой в ​​качестве базового регистра.

Обратите внимание, что строки по умолчанию в Haskell - это ленивые последовательности символов, что может быть не так вScala, но я ожидаю, что подобное будет возможно, поскольку функциональная сторона Scala основана на той же общей традиции, вдохновленной ML, что и Haskell.


EDIT : Кстати,Заметьте, что вопреки ожиданиям многих функциональных программистов, моя реализация является , а не хвостовой рекурсией, и это и намеренно, и правильно.Вместо этого он выполняет рекурсивный вызов в конце списка, и поскольку конструкторы данных в Haskell позволяют ленивым вещам, каждый выходной символ генерируется по требованию, а остальная часть сгиба лениво откладывается, и все это выполняется в пространстве постоянного стека.Конечный результат - это, по сути, итеративный цикл, потребляющий элементы из входного потока , но написанный так, чтобы он выглядел как простая рекурсивная функция.

Даже если в общем случае вы бы так не поступилислучай кроме как в Haskell, ленивые списки / генераторы / и т.д.в наши дни распространены во многих языках, и выражение «потреблять конечное количество, обрабатывать его, производить вывод» для преобразования таких потоков не зависит от языка.

Кроме того, я благодарен Анторасу и Луиджи Плинге за написание.Реализации Scala с похожими алгоритмами - помогают мне лучше понять Scala, с которой я в настоящее время знаком лишь немного.

2 голосов
/ 17 августа 2011

Конечно, вы можете просто использовать regex:

import util.matching.Regex.Match

def camel(s: String) = "_(.)".r.replaceAllIn(s, (m: Match) => m.group(1).toUpperCase)

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

def camel(s: String) = "_(.)".r replaceAllIn (s, _ group 1 toUpperCase)
2 голосов
/ 17 августа 2011

Я перевел код Haskell CA McCann в Scala:

def capitalize: (Char, Seq[Char]) => Seq[Char] = {
  case ('_', Seq(x, xs @ _*)) => x.toUpper +: xs
  case (c, xs) => c +: xs
}

def convertNumber: String => String =
  _.foldRight(Seq.empty[Char]) { capitalize } mkString

Seq("invoice_number", "ident", "some_other_stuff") map convertNumber foreach println

Поскольку String является IndexedSeq в Scala, добавление к нему занимает практически постоянное время.Код Luigi может быть обновлен до чего-то такого:

def capitalize(xs: Seq[Char], ys: Seq[Char]): Seq[Char] = xs match {
  case Seq('_', x, xs @ _*) => capitalize(xs, ys :+ x.toUpper)
  case Seq(x, xs @ _*) => capitalize(xs, ys :+ x)
  case _ => ys
}

def convertNumber(s: String): String =
  capitalize(s, Seq.empty).mkString
2 голосов
/ 17 августа 2011

Мой хвост рекурсивный удар:

def camel(s: String) = {
  def rec(s: Seq[Char], res: Seq[Char]): String = s match {
    case Nil           => res.reverse.mkString
    case '_' :: x :: t => rec(t, x.toUpper +: res)
    case x :: t        => rec(t, x +: res)
  }
  rec(s.toList, "")
}

println(camel("hay_guise k_thx_bai")) // hayGuise kThxBai

По какой-то причине "string".toSeq не соответствует, но .toList соответствует; Может быть, кто-то может объяснить, почему.

Редактировать: или это тоже работает:

def camel(s:String) = {
  val it = s.iterator
  it.map {
    case '_' => if(it.hasNext) it.next.toUpper else ""
    case x => x
  }.mkString
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...