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



嗨,我是 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
}
}

我知道这不是解决此问题的最优化方法。此外,我正在寻找一种使用尾递归对此进行编码的方法,其中我传递两个数字并获取落在这两个数字之间的所有好数字。 这能以更优化的方式完成吗? 提前感谢!

不需要String转换。

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

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

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

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

使用此函数:(这将是有效的方法,因为函数forall不会遍历整个数字列表falsev(i)>v.drop(i+1).sum

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)

这是一种更实用的方式。pairs是数字和以下数字之和之间的元组列表。使用拖放、获取和切片(拖放和获取的组合(方法很容易创建这些元组。

最后,我可以用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集合从 Range 更改为 Stream。

(1 until str.length).toStream

函数式风格倾向于喜欢一元类型的东西,比如map和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(_))

最新更新