如何使用函数式编程方法在Scala中重写此代码

时间:2021-12-25 22:04:55

Below is code snippet that does some URL normalization. How to rewrite it to use immutable variables only?

下面是执行一些URL规范化的代码片段。如何重写它只使用不可变变量?

Of course, without making it larger or more complex.

当然,不要让它更大或更复杂。

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
}

6 个解决方案

#1


If you wanted to chain a bunch of these if/then conditions together to modify a string you could consider adding an implicit class to handle the if/then evaluations like so:

如果你想将一堆if / then条件链接起来修改一个字符串,你可以考虑添加一个隐式类来处理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("\\\\", "/")
}

This would be overkill if you only had these two conditions to evaluate, but might be nice if you had more and this was a common pattern.

如果你只考虑这两个条件,这将是一种过度杀伤,但如果你有更多的条件可能会很好,这是一个常见的模式。

#2


Consider name of functional programming! The whole point is that you replace variables with functions.

考虑函数式编程的名称!重点是用函数替换变量。

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 points out, the last function could also be written as

CM Baxter指出,最后一个函数也可以写成

private val normalizeUrl = 
   removeEndingSlash _ andThen 
   normalizeProtocol andThen 
   replaceSlash

I leave it to you to decide which is more legible.

我留给你决定哪个更清晰。

#3


Consider for instance an intuitive approach with intermediate results, and if-else expressions,

例如,考虑一种具有中间结果的直观方法,以及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("\\\\", "/")
}

Note the last expression, url2 with replaceAll, is returned.

请注意返回最后一个带有replaceAll的表达式url2。

#4


What about some refactoring and chaining? Your var url is not necessary here.

一些重构和链接怎么样?这里不需要你的var url。

I think this would work:

我认为这会奏效:

private def normalizeUrl(url: String) = {
  (if (url.indexOf(':') < 0 || url.indexOf(':') == 1) {
    "file:"
  } else {
    ""
  }) + (if (url.endsWith("/")) {
    url.dropRight(1)
  } else {
    url
  }).replaceAll("\\\\", "/")
}

Of course, for better readability, I would suggest the use of something like this:

当然,为了更好的可读性,我建议使用这样的东西:

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("\\\\", "/")
}

Don't be afraid to use more than one val's. :)

不要害怕使用多个val。 :)

#5


One option would be to use the Reader Monad and map the functions over it:

一种选择是使用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("\\\\", "/"))

Then just call it like any function:

然后就像任何函数一样调用它:

normalizeUrl("some\\\\url")

I think I would prefer elm's solution though

我想我更喜欢榆树的解决方案

#6


Here is another one. You write tests for reach of your functions ( removeEndSlash, removeSlashes and removeSlashes

这是另一个。您编写了函数范围的测试(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)))

  }

#1


If you wanted to chain a bunch of these if/then conditions together to modify a string you could consider adding an implicit class to handle the if/then evaluations like so:

如果你想将一堆if / then条件链接起来修改一个字符串,你可以考虑添加一个隐式类来处理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("\\\\", "/")
}

This would be overkill if you only had these two conditions to evaluate, but might be nice if you had more and this was a common pattern.

如果你只考虑这两个条件,这将是一种过度杀伤,但如果你有更多的条件可能会很好,这是一个常见的模式。

#2


Consider name of functional programming! The whole point is that you replace variables with functions.

考虑函数式编程的名称!重点是用函数替换变量。

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 points out, the last function could also be written as

CM Baxter指出,最后一个函数也可以写成

private val normalizeUrl = 
   removeEndingSlash _ andThen 
   normalizeProtocol andThen 
   replaceSlash

I leave it to you to decide which is more legible.

我留给你决定哪个更清晰。

#3


Consider for instance an intuitive approach with intermediate results, and if-else expressions,

例如,考虑一种具有中间结果的直观方法,以及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("\\\\", "/")
}

Note the last expression, url2 with replaceAll, is returned.

请注意返回最后一个带有replaceAll的表达式url2。

#4


What about some refactoring and chaining? Your var url is not necessary here.

一些重构和链接怎么样?这里不需要你的var url。

I think this would work:

我认为这会奏效:

private def normalizeUrl(url: String) = {
  (if (url.indexOf(':') < 0 || url.indexOf(':') == 1) {
    "file:"
  } else {
    ""
  }) + (if (url.endsWith("/")) {
    url.dropRight(1)
  } else {
    url
  }).replaceAll("\\\\", "/")
}

Of course, for better readability, I would suggest the use of something like this:

当然,为了更好的可读性,我建议使用这样的东西:

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("\\\\", "/")
}

Don't be afraid to use more than one val's. :)

不要害怕使用多个val。 :)

#5


One option would be to use the Reader Monad and map the functions over it:

一种选择是使用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("\\\\", "/"))

Then just call it like any function:

然后就像任何函数一样调用它:

normalizeUrl("some\\\\url")

I think I would prefer elm's solution though

我想我更喜欢榆树的解决方案

#6


Here is another one. You write tests for reach of your functions ( removeEndSlash, removeSlashes and removeSlashes

这是另一个。您编写了函数范围的测试(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)))

  }