如何使用函数式编程方法在 Scala 中重写此代码
How to rewrite this code in Scala using a Functional Programming approach
下面是执行一些 URL 规范化的代码片段。如何重写它以仅使用不可变变量?
当然,不用让它变得更大或更复杂。
private def normalizeUrl(url0: String) = {
var url = url0
if (url.endsWith("/")) {
url = url.dropRight(1)
}
if (url.indexOf(':') < 0 ||
url.indexOf(':') == 1) { //windows absolute path
url = "file:" + url;
}
url = url.replaceAll("\\", "/");
url
}
例如,考虑一种具有中间结果和 if-else
表达式的直观方法,
private def normalizeUrl(url0: String) = {
val url1 =
if (url0.endsWith("/")) url0.dropRight(1)
else url0
val url2 =
if (url1.indexOf(':') < 0 || url1.indexOf(':') == 1) "file:" + url1
else url1
url2.replaceAll("\\", "/")
}
注意返回最后一个表达式 url2
和 replaceAll
。
重构和链接怎么样?您的 var url
在这里没有必要。
我认为这可行:
private def normalizeUrl(url: String) = {
(if (url.indexOf(':') < 0 || url.indexOf(':') == 1) {
"file:"
} else {
""
}) + (if (url.endsWith("/")) {
url.dropRight(1)
} else {
url
}).replaceAll("\\", "/")
}
当然,为了更好的可读性,我建议使用这样的东西:
private def normalizeUrl(url: String) = {
val prefix = if (url.indexOf(':') < 0 || url.indexOf(':') == 1) "file:" else ""
val noSlash = if (url.endsWith("/")) url.dropRight(1) else url
(prefix + noSlash).replaceAll("\\", "/")
}
不要害怕使用多个 val。 :)
如果您想将这些 if/then 条件链接在一起以修改字符串,您可以考虑添加隐式 class 来处理 if/then 评估,如下所示:
object UrlPimping{
implicit class PimpedUrl(val url:String) extends AnyVal{
def changeIf(condition:String => Boolean)(f:String => String):String = {
if (condition(url)) f(url)
else url
}
}
}
private def normalizeUrl(url: String) = {
import UrlPimping._
url.
changeIf(_.endsWith("/"))(_.dropRight(1)).
changeIf(u => u.indexOf(':') < 0 || u.indexOf(':') == 1)(u => s"file:$u").
replaceAll("\\", "/")
}
如果您只有这两个条件来评估,这就太过分了,但如果您有更多条件,这可能会很好,而且这是一种常见的模式。
这是另一个。您编写测试以达到您的功能(removeEndSlash
、removeSlashes
和 removeSlashes
def normalizeURL(url: String) = {
def removeEndSlash(u: String): String = if (u.endsWith("/")) u.dropRight(1) else u
def isFile(u: String): String = {
val idx = u.indexOf(':')
if (idx < 0 || idx == 1)
"file:" + u
else
u
}
def removeSlashes( u : String ) = u.replaceAll("\\", "/")
removeSlashes(isFile(removeEndSlash(url)))
}
考虑函数式编程的名称!重点是你用函数替换变量。
private def normalizeProtocol(url: String) =
if (url.endsWith("/")) url.dropRight(1) else url
private def removeEndingSlash(url: String) =
if (url.indexOf(':') < 0 ||
url.indexOf(':') == 1) //windows absolute path
"file:" + url
else
url
private def replaceSlash(url: String) =
url.replaceAll("\\", "/");
private def normalizeUrl(url: String) =
replaceSlash(normalizeProtocol(removeEndingSlash(url)))
CM Baxter 指出,最后一个函数也可以写成
private val normalizeUrl =
removeEndingSlash _ andThen
normalizeProtocol andThen
replaceSlash
我留给你来决定哪个更清晰。
一个选择是使用 Reader Monad 并将函数映射到它上面:
val normalizeUrl: Reader[String, String] = Reader[String, String](s => s)
.map(url => if (url.endsWith("/")) { url.dropRight(1) } else url)
.map(url => if (url.indexOf(':') < 0 || url.indexOf(':') == 1) "file:" + url else url)
.map(url => url.replaceAll("\\", "/"))
然后像调用任何函数一样调用它:
normalizeUrl("some\\url")
不过我想我更喜欢 elm 的解决方案
下面是执行一些 URL 规范化的代码片段。如何重写它以仅使用不可变变量?
当然,不用让它变得更大或更复杂。
private def normalizeUrl(url0: String) = {
var url = url0
if (url.endsWith("/")) {
url = url.dropRight(1)
}
if (url.indexOf(':') < 0 ||
url.indexOf(':') == 1) { //windows absolute path
url = "file:" + url;
}
url = url.replaceAll("\\", "/");
url
}
例如,考虑一种具有中间结果和 if-else
表达式的直观方法,
private def normalizeUrl(url0: String) = {
val url1 =
if (url0.endsWith("/")) url0.dropRight(1)
else url0
val url2 =
if (url1.indexOf(':') < 0 || url1.indexOf(':') == 1) "file:" + url1
else url1
url2.replaceAll("\\", "/")
}
注意返回最后一个表达式 url2
和 replaceAll
。
重构和链接怎么样?您的 var url
在这里没有必要。
我认为这可行:
private def normalizeUrl(url: String) = {
(if (url.indexOf(':') < 0 || url.indexOf(':') == 1) {
"file:"
} else {
""
}) + (if (url.endsWith("/")) {
url.dropRight(1)
} else {
url
}).replaceAll("\\", "/")
}
当然,为了更好的可读性,我建议使用这样的东西:
private def normalizeUrl(url: String) = {
val prefix = if (url.indexOf(':') < 0 || url.indexOf(':') == 1) "file:" else ""
val noSlash = if (url.endsWith("/")) url.dropRight(1) else url
(prefix + noSlash).replaceAll("\\", "/")
}
不要害怕使用多个 val。 :)
如果您想将这些 if/then 条件链接在一起以修改字符串,您可以考虑添加隐式 class 来处理 if/then 评估,如下所示:
object UrlPimping{
implicit class PimpedUrl(val url:String) extends AnyVal{
def changeIf(condition:String => Boolean)(f:String => String):String = {
if (condition(url)) f(url)
else url
}
}
}
private def normalizeUrl(url: String) = {
import UrlPimping._
url.
changeIf(_.endsWith("/"))(_.dropRight(1)).
changeIf(u => u.indexOf(':') < 0 || u.indexOf(':') == 1)(u => s"file:$u").
replaceAll("\\", "/")
}
如果您只有这两个条件来评估,这就太过分了,但如果您有更多条件,这可能会很好,而且这是一种常见的模式。
这是另一个。您编写测试以达到您的功能(removeEndSlash
、removeSlashes
和 removeSlashes
def normalizeURL(url: String) = {
def removeEndSlash(u: String): String = if (u.endsWith("/")) u.dropRight(1) else u
def isFile(u: String): String = {
val idx = u.indexOf(':')
if (idx < 0 || idx == 1)
"file:" + u
else
u
}
def removeSlashes( u : String ) = u.replaceAll("\\", "/")
removeSlashes(isFile(removeEndSlash(url)))
}
考虑函数式编程的名称!重点是你用函数替换变量。
private def normalizeProtocol(url: String) =
if (url.endsWith("/")) url.dropRight(1) else url
private def removeEndingSlash(url: String) =
if (url.indexOf(':') < 0 ||
url.indexOf(':') == 1) //windows absolute path
"file:" + url
else
url
private def replaceSlash(url: String) =
url.replaceAll("\\", "/");
private def normalizeUrl(url: String) =
replaceSlash(normalizeProtocol(removeEndingSlash(url)))
CM Baxter 指出,最后一个函数也可以写成
private val normalizeUrl =
removeEndingSlash _ andThen
normalizeProtocol andThen
replaceSlash
我留给你来决定哪个更清晰。
一个选择是使用 Reader Monad 并将函数映射到它上面:
val normalizeUrl: Reader[String, String] = Reader[String, String](s => s)
.map(url => if (url.endsWith("/")) { url.dropRight(1) } else url)
.map(url => if (url.indexOf(':') < 0 || url.indexOf(':') == 1) "file:" + url else url)
.map(url => url.replaceAll("\\", "/"))
然后像调用任何函数一样调用它:
normalizeUrl("some\\url")
不过我想我更喜欢 elm 的解决方案