узнать, является ли число хорошим числом в скале - PullRequest
0 голосов
/ 18 сентября 2018

Привет, я новичок в методологии функционального программирования в 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 голосов
/ 19 сентября 2018

Вот чисто числовая версия, использующая рекурсивную функцию.

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

Это должно скомпилировать в эффективный цикл.

0 голосов
/ 18 сентября 2018

Нет 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
0 голосов
/ 18 сентября 2018

Использование этой функции: (Это будет эффективный способ, так как функция 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)
0 голосов
/ 18 сентября 2018

Функциональный стиль имеет тенденцию предпочитать вещи монадического типа, такие как карты и сокращения.Чтобы это выглядело функционально и понятно, я бы сделал что-то вроде:

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(_))
0 голосов
/ 18 сентября 2018

Это более функциональный способ.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 с Range на Stream.

(1 until str.length).toStream
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...