在scala中生成给定字符串的所有IP地址

时间:2014-04-22 15:12:20

标签: scala recursion

我正在尝试用一串数字写一个IP生成器。生成器将输入一串数字,例如“17234”,并将返回所有可能的ips列表,如下所示:

1.7.2.34
1.7.23.4
1.72.3.4
17.2.3.4

我尝试编写一个片段来进行生成,如下所示:

def genip(ip:String):Unit = {
    def legal(ip:String):Boolean = (ip.size == 1) || (ip.size == 2) || (ip.size == 3)
    def genips(ip:String,portion:Int,accum:String):Unit = portion match {
        case 1 if legal(ip) => println(accum+ip)
        case _ if portion > 1 => {
            genips(ip.drop(1),portion-1,if(accum.size == 0) ip.take(1)+"." else accum+ip.take(1)+".")
            genips(ip.drop(2),portion-1,if(accum.size == 0) ip.take(2)+"." else accum+ip.take(2)+".")
            genips(ip.drop(3),portion-1,if(accum.size == 0) ip.take(3)+"." else accum+ip.take(3)+".")
        }
        case _ => return
    }
    genips(ip,4,"")
}

我们的想法是将字符串分成四个八位字节,然后将八位字节进一步分区为大小为"1","2" and "3"的字符串,然后以递归方式下降到剩余的字符串中。

我不确定自己是否走在正确的轨道上,但如果有人能够提出一种更实用的方法,那就更好了。

由于

2 个答案:

答案 0 :(得分:2)

以下是附加代码的替代版本:

  def generateIPs(digits : String) : Seq[String] = generateIPs(digits, 4)

  private def generateIPs(digits : String, partsLeft : Int) : Seq[String] = {
    if ( digits.size < partsLeft || digits.size > partsLeft * 3) {
      Nil
    } else if(partsLeft == 1) {
      Seq(digits)
    } else {
      (1 to 3).map(n => generateIPs(digits.drop(n), partsLeft - 1)
        .map(digits.take(n) + "." + _)
      ).flatten
    }
  }

  println("Results:\n" + generateIPs("17234").mkString("\n"))

主要变化:

  • 方法现在返回字符串集合(而不是单位),因此它们是正确的函数(而不是副作用)并且可以轻松测试;

  • 根据我们拍摄的一组数字的大小,避免重复相同的代码3次;

  • 没有将累积的中间结果作为方法参数传递 - 在这种情况下它没有意义,因为你最多有4个递归调用,没有它就更容易阅读,尽管你丢失了在很多情况下,尾递归可能是合理的。

注意:最后map语句是被for理解所取代的好候选者,许多开发人员发现这些理解更容易阅读和推理,尽管我将其留作练习:)< / p>

答案 1 :(得分:1)

你的代码是正确的想法;我不确定它的功能是否对任何事情都有帮助,但我会展示出功能和副作用的方法来做你想做的事情。首先,我们想要一个很好的例程来确定一些数字,确保剩下的分组留下一个好的数字,并确保它们在IP的范围内:

def validSize(i: Int, len: Int, more: Int) = i + more <= len && i + 3*more >= len
def chunk(s: String, more: Int) = {
  val parts = for (i <- 1 to 3 if validSize(i, s.length, more)) yield s.splitAt(i)
  parts.filter(_._1.toInt < 256)
}

现在我们需要递归四次使用chunk来生成可能性。这是一个内部可迭代的迭代解决方案:

def genIPs(digits: String) = {
  var parts = List(("", digits))
  for (i <- 1 to 4) {
    parts = parts.flatMap{ case (pre, post) =>
      chunk(post, 4-i).map{ case (x,y) => (pre+x+".", y) }
    }
  }
  parts.map(_._1.dropRight(1))
}

这是使用Iterator进行递归的方法:

def genIPs(digits: String) = Iterator.iterate(List((3,"",digits))){ _.flatMap{
  case(j, pre, post) => chunk(post, j).map{ case(x,y) => (j-1, pre+x+".", y) }
}}.dropWhile(_.head._1 >= 0).next.map(_._2.dropRight(1))

逻辑是相同的。这是工作:

scala> genIPs("1238516")
res2: List[String] = List(1.23.85.16, 1.238.5.16, 1.238.51.6,
                          12.3.85.16, 12.38.5.16, 12.38.51.6,
                          123.8.5.16, 123.8.51.6, 123.85.1.6)