我编写了一个简单的脚本,用于将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”。感谢。
答案 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)