找出一个数字在scala中是否是一个好数字

时间:2018-09-18 14:40:17

标签: scala functional-programming

嗨,我是scala函数编程方法的新手。我想在函数中输入一个数字,然后检查它是否是一个好数字。 如果数字的每个数字都大于该数字右侧的数字总和,那么它就是一个好数字。 例如: 9620等于(2> 0,6> 2 + 0,9> 6 + 2 + 0) 我用于解决此问题的步骤是

1. converting a number to string and reversing it
2. storing all digits of the reversed number as elements of a list
3. applying for loop from  i equals 1 to length of number - 1
4. calculating sum of first i digits as num2
5. extracting ith digit from the list as digit1 which is one digit ahead of the first i numbers for which we calculated sum because list starts from zero.
6. comparing output of 4th and 5th step. if num1 is greater than num2 then we will break the for loop and come out of the loop to print it is not a good number.

请在下面找到我的代码

val num1 = 9521.toString.reverse
val list1 = num1.map(_.todigit).toList
for (i <- 1 to num1.length - 1) {
  val num2 = num1.take(i).map(_.toDigits) sum
  val digit1 = list1(i)
  if (num2 > digit1) {
    print("number is not a good number")
    break
  }
}

我知道这不是解决此问题的最优化方法。我也在寻找一种使用尾部递归进行编码的方法,在该方法中我传递了两个数字,并使所有好的数字都落在这两个数字之间。 能否以更优化的方式完成? 预先感谢!

5 个答案:

答案 0 :(得分:1)

No String conversions required.

val n = 9620
val isGood = Stream.iterate(n)(_/10)
                   .takeWhile(_>0)
                   .map(_%10)
                   .foldLeft((true,-1)){ case ((bool,sum),digit) =>
                      (bool && digit > sum, sum+digit)
                   }._1

答案 1 :(得分:0)

这是一种更实用的方法。 pairs是一个数字和随后的数字之和之间的元组列表。使用drop,take和slice(drop和take的组合)方法来创建这些元组很容易。

最后,我可以使用forall方法来表达自己的状况。

val n = 9620

val str = n.toString

val pairs = for { x <- 1 until str.length } yield (str.slice(x - 1, x).toInt, str.drop(x).map(_.asDigit).sum)

pairs.forall { case (a, b) => a > b }

如果您想发挥功能和表现力,请避免使用break。如果您需要检查每个元素的条件是将问题转移到集合中的一个好主意,则可以使用forAll

不是这种情况,但是如果您想提高性能(如果您不想创建整个对集合,因为第一个元素的条件为false),则可以从范围更改for集合流。

(1 until str.length).toStream

答案 2 :(得分:0)

使用此功能:(这将是一种有效的方法,因为功能forall不会遍历整个数字列表。当立即发现错误条件时(即,当 v(i)>v.drop(i+1).sum 变为 false ),同时从向量v的左向右遍历。

  def isGood(n: Int)= {
   val v1 = n.toString.map(_.asDigit)
   val v = if(v1.last!=0) v1 else v1.dropRight(1)
   (0 to v.size-1).forall(i=>v(i)>v.drop(i+1).sum)
  }

如果我们要查找整数范围为n1 to n2的良好数字,则可以使用以下函数:

def goodNums(n1:Int,n2:Int) = (n1 to n2).filter(isGood(_))

在Scala REPL中:

scala> isGood(9620)
res51: Boolean = true

scala> isGood(9600)
res52: Boolean = false

scala> isGood(9641)
res53: Boolean = false

scala> isGood(9521)
res54: Boolean = true

scala> goodNums(412,534)
res66: scala.collection.immutable.IndexedSeq[Int] = Vector(420, 421, 430, 510, 520, 521, 530, 531)

scala> goodNums(3412,5334)
res67: scala.collection.immutable.IndexedSeq[Int] = Vector(4210, 5210, 5310)

答案 3 :(得分:0)

功能样式趋向于喜欢单子类型的事物,例如maps和reduce。为了使它看起来实用且清晰,我会做类似的事情:

def isGood(value: Int) =
  value.toString.reverse.map(digit=>Some(digit.asDigit)).
    reduceLeft[Option[Int]]
    {
      case(sum, Some(digit)) => sum.collectFirst{case sum if sum < digit => sum+digit}
    }.isDefined

不要使用尾部递归来计算范围,而只是生成范围,然后对其进行过滤:

def goodInRange(low: Int, high: Int) = (low to high).filter(isGood(_))

答案 4 :(得分:0)

这里是使用递归函数的纯数字版本。

def isGood(n: Int): Boolean = {
  @tailrec
  def loop(n: Int, sum: Int): Boolean =
    (n == 0) || (n%10 > sum && loop(n/10, sum + n%10))

  loop(n/10, n%10)
}

这应该编译成一个有效的循环。