Как вернуть функцию в Scala - PullRequest
17 голосов
/ 24 ноября 2010

Как я могу вернуть функцию лексическое закрытие с побочным эффектом 1 в Scala?

Например, я смотрел этот пример кода в Go :

...    
// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return b
    }
}
...
println(f(), f(), f(), f(), f())

печатает 1 2 3 5 8

И я могу 'Не могу понять, как написать то же самое в Scala.

1.Исправлено после Apocalisp комментарий

Ответы [ 5 ]

21 голосов
/ 24 ноября 2010

Немного короче, возврат вам не нужен.

def fib() = {
    var a = 0
    var b = 1
    () => { 
        val t = a;
        a = b
        b = t + b
        b
    }
}
20 голосов
/ 24 ноября 2010

Г!Изменяемые переменные?!

val fib: Stream[Int] =
  1 #:: 1 #:: (fib zip fib.tail map Function.tupled(_+_))

Вы можете вернуть литеральную функцию, которая получает n-ый фиб, например:

val fibAt: Int => Int = fib drop _ head

РЕДАКТИРОВАТЬ: Поскольку вы запросили функциональный способ "полученияразные значения каждый раз, когда вы звоните ", вот как вы это сделаете.При этом используется State монада Scalaz:

import scalaz._
import Scalaz._

def uncons[A](s: Stream[A]) = (s.tail, s.head)
val f = state(uncons[Int])

Значение f является функцией перехода состояния.Учитывая поток, он вернет свою голову и "изменит" поток на стороне, взяв его за хвост.Обратите внимание, что f полностью игнорирует fib.Вот сеанс REPL, иллюстрирующий, как это работает:

scala> (for { _ <- f; _ <- f; _ <- f; _ <- f; x <- f } yield x)
res29: scalaz.State[scala.collection.immutable.Stream[Int],Int] = scalaz.States$$anon$1@d53513

scala> (for { _ <- f; _ <- f; _ <- f; x <- f } yield x)
res30: scalaz.State[scala.collection.immutable.Stream[Int],Int]  = scalaz.States$$anon$1@1ad0ff8

scala> res29 ! fib
res31: Int = 5

scala> res30 ! fib
res32: Int = 3

Очевидно, что значение, которое вы получаете, зависит от того, сколько раз вы звоните f.Но это все чисто функционально и потому модульно и составно.Например, мы можем пропустить любой непустой поток, а не только fib.

Итак, вы видите, что вы можете иметь эффекты без побочных эффектов.

8 голосов
/ 24 ноября 2010

Пока мы делимся классными реализациями функции Фибоначчи, которые только косвенно связаны с вопросом, вот памятная версия:

val fib: Int => BigInt = {                         
   def fibRec(f: Int => BigInt)(n: Int): BigInt = {
      if (n == 0) 1 
      else if (n == 1) 1 
      else (f(n-1) + f(n-2))                           
   }                                                     
   Memoize.Y(fibRec)
}

В ней используется запоминающий комбинатор с фиксированной точкой, реализованный как ответ наэтот вопрос: В Scala 2.8, какой тип использовать для хранения таблицы изменяемых данных в памяти?

Между прочим, реализация комбинатора предлагает несколько более явный метод для реализации вашего функция лексическое закрытие с побочным эффектом:

def fib(): () => Int = {
   var a = 0
   var b = 1
   def f(): Int = {
      val t = a;
      a = b
      b = t + b
      b
  }
  f
}
3 голосов
/ 24 ноября 2010

Понял !! после проб и ошибок:

def fib() : () => Int = {
    var a = 0
    var b = 1
    return (()=>{ 
        val t = a;
        a = b
        b = t + b
        b
    })
}

Тестирование:

val f = fib()
println(f(),f(),f(),f())

1 2 3 5 8
1 голос
/ 24 ноября 2010

При использовании кортежа вам не нужна временная переменная:

def fib() = {
  var t = (1,-1)
  () => { 
    t = (t._1 + t._2, t._1)
    t._1
  }
}

Но в реальной жизни вам следует использовать решение Apocalisp.

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