簡體   English   中英

如何使用函數式編程方法在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 / 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("\\\\", "/")
}

如果你只考慮這兩個條件,這將是一種過度殺傷,但如果你有更多的條件可能會很好,這是一個常見的模式。

考慮函數式編程的名稱 重點是用函數替換變量。

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

我留給你決定哪個更清晰。

例如,考慮一種具有中間結果的直觀方法,以及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("\\\\", "/")
}

請注意返回最后一個帶有replaceAll表達式url2

一些重構和鏈接怎么樣? 這里不需要你的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。 :)

一種選擇是使用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")

我想我更喜歡榆樹的解決方案

這是另一個。 你寫的測試為你的函數(REACH removeEndSlashremoveSlashesremoveSlashes

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)))

  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM