Подтвердить что ты не робот

Как написать функцию, имитирующую цикл while в Scala

Как я могу написать функцию, которая имитирует цикл while? Он должен принимать 2 аргумента: условие и выражение для выполнения.

Я попробовал следующее:

val whileLoop: (Boolean,Any)=>Unit = (condition:Boolean, expression:Any) => {
 expression
 if(condition) whileLoop(condition,expression)
 () }    

Но, похоже, он не работает, например. У меня есть массив:

val arr = Array[Int](-2,5,-5,9,-3,10,3,4,1,2,0,-20)    

Также у меня есть переменная i:

var i = 0

Я хочу напечатать все элементы обр. Я могу сделать это со следующим кодом:

while(i<arr.length) { println(tab(i)); i+=1 }

Я хотел бы сделать то же самое, используя мою функцию whileLoop. Но я не могу написать функцию, которая ссылается на переменную и модифицирует ее. Я мог бы передать это, используя массив только с одним элементом, например.

val nr = Array(0)

и функция:

val printArray: Array[Int]=>Unit = (n:Array[Int]) => {
 println(arr(n(0)))
 n(0)+=1
 ()
}

а затем используя мой whileLoop:

whileLoop(nr(0)<arr.length, printArray)

После использования вышеприведенных кодов я получаю StackOverflowError, а nr (0) равно нулю. Также следующая функция:

val printArray: Array[Int]=>Unit = (n:Array[Int]) => {
 println(arr(nr(0)))
 nr(0)+=1
 ()
}

дает тот же результат.

Как я могу написать правильную функцию whileLoop и использовать ее для печати всех элементов arr?

Заранее благодарим за советы.

4b9b3361

Ответ 1

Основная проблема с вашей реализацией заключается в том, что условие и выражение оцениваются только один раз, когда вы сначала вызываете whileLoop. В рекурсивном вызове вы просто передаете значение, а не выражение.

Вы можете решить это, используя аргументы by-name:

def whileLoop(cond : =>Boolean, block : =>Unit) : Unit =
  if(cond) {
    block
    whileLoop(cond, block)
  }

В качестве примера:

scala> val a = Array(1, 2, 3)
scala> var i = 0
scala> whileLoop(i < a.length, { println(i); i += 1 })
1
2
3

Обратите внимание, что переменные a и i корректно ссылаются. Внутри компилятор Scala построил функцию как для условия, так и для выражения (блока), и эти функции поддерживают ссылку на их среду.

Также обратите внимание на то, что для большей синтаксической чувствительности к сахару вы можете определить whileLoop как отображаемую функцию:

def whileLoop(cond : =>Boolean)(block : =>Unit) : Unit =
  if(cond) {
    block
    whileLoop(cond)(block)
  }

Это позволяет вам называть его точно так же, как фактический цикл while:

whileLoop(i < a.length) {
  println(a(i))
  i += 1
}

Ответ 2

Вот что я придумал: Прежде всего, ваша функция нуждается в этих 4 аргументах:

- array which is yet to be processed
- predicate that tells the function when to stop
- function that takes the array to be processed and current state and produces a new state
- and state that is being propagated through the recurion:

Я думаю, что код довольно понятен:

def whileFunc[A,B](over: Array[A], predicate: Array[A] => Boolean, apply: (Array[A],B) => B, state: B):B = {
   val doIterate = predicate(over)
   if(doIterate) whileFunc(over.tail, predicate, apply, apply(over,state)) else state
}

это можно сделать намного приятнее, но я старался держать его как можно проще. Чтобы подсчитать все элементы массива, вы бы назвали его так:

scala>     whileFunc(Array(1,2,3), (a:Array[Int]) => !a.isEmpty,(a:Array[Int],s: Int) => s + a.head, 0)
res5: Int = 6

для печати каждого из элементов:

whileFunc[Int, Unit](Array(1,2,3), (a:Array[Int]) => !a.isEmpty,(a:Array[Int],s: Unit) => print(a.head), Unit)
123

Кстати, если вас интересует такой материал, я бы порекомендовал вам покупать функциональное программирование в Scala, есть две главы, которые заставляют вас реализовывать такие функции. Это очень весело.