如何使这个代码功能?

时间:2011-08-17 15:09:04

标签: scala functional-programming

我编写了一个简单的脚本,用于将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()
}

我想知道如何使这个代码功能化。我想不出任何更高阶函数来替换“如果&lt; some-condition&gt; else增加1”逻辑,则将“i增加2”。感谢。

6 个答案:

答案 0 :(得分:6)

好的,这是我的方法。

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)

认为它是合理的功能风格。有趣的scala大师将如何解决这个问题:)

答案 1 :(得分:4)

我为不使用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中的数据构造函数让事情变得懒惰,所以每个输出字符都是按需生成的,其余的折叠延迟延迟,并且整个事件在常量堆栈空间中运行。最终结果本质上是一个迭代循环,它消耗来自输入流的元素,但写成看起来像一个简单的递归函数。

即使你不会在一般情况下这样做,除了在Haskell,懒惰列表/生成器/等。这些天在许多语言中都很常见,而且“消耗有限数量,处理它,产生输出”的习惯用于转换这些流是与语言无关的。

另外,我感谢Antoras和Luigi Plinge用类似算法编写Scala实现 - 这有助于我更好地了解Scala,我目前只是熟悉它。

答案 2 :(得分:4)

另一种解决方案;使用滑动迭代器窗口:

  println(
    (" ".iterator ++ io.Source.fromFile(file)).sliding(2).map {
      s => if (s(0) == '_') s(1).toUpper else s(1)
    }.filter(_ != '_').mkString)

答案 3 :(得分:2)

我的尾巴递归刺伤:

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
}

答案 4 :(得分:2)

我将C. A. McCann的Haskell代码翻译成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是Scala中的IndexedSeq,所以追加它需要有效的恒定时间。 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

答案 5 :(得分:2)

当然,您可以使用正则表达式:

import util.matching.Regex.Match

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

编辑: 或者如果你使用匿名函数,它会更短,你不需要导入(踢点无点):

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