2011-03-17 27 views

Respuesta

47

Como se describe en el enlace informados. Puedes usar Y-combinator. Aquí está el ejemplo:

scala> def fix[A,B](f: (A=>B)=>(A=>B)): A=>B = f(fix(f))(_) 
fix: [A,B](f: ((A) => B) => (A) => B)(A) => B 

scala> val fact = fix[Int,Int](f => a => if(a<=0) 1 else f(a-1) * a) 
fact: (Int) => Int = <function1> 

scala> fact(12) 
res0: Int = 479001600 

Tenga en cuenta que no funciona con números grandes. Tenga cuidado con la optimización de la cola de llamadas.

+0

Wow, amaizing. Gracias. – aioobe

+9

"Asombroso" en el sentido original de hacerme sentir como si estuviera perdido en un laberinto. 'fix' es una función que toma como entrada una función que toma un solo argumento y devuelve otra función que toma un solo argumento, y luego ... OK, voy a necesitar una mejor explicación de * alguien * .. – Malvolio

+0

Pero esto no permite la optimización de la cola de llamadas, ¿estoy en lo correcto? – fresskoma

21

Si no desea presionar "Matemáticas asombrosas", puede volver a los aspectos del objeto de Scala.

val fact = new Function1[Int,Int]{ 
    def apply(x:Int):Int = if(x==1) x else x * apply(x-1) 
} 
12

con el fin de hacer que se vea más geeks también puede utilizar este estilo de código:

val fact = new ((Int) => Int){ 
    def apply(x:Int):Int = if(x==1) x else x * apply(x-1) 
} 
+0

Este se ve mucho mejor que simplemente 'nueva función [Int , Int] {...} '. ¡Gracias! –

5

Agregando a las muchas buenas respuestas aquí en este hilo, el hecho de que Scala no nos está dando llamada de cola optimizable El combinador de punto fijo me ha estado molestando tanto que he decidido escribir una macro para traducir la llamada tipo Y-combinator a una llamada recursiva ordinaria e idiomática (con la optimización de la cola de llamada, por supuesto). La idea es que una llamada como

fix[Int,Int]((next) => (y) => ...body...) 

es fácilmente traducible a

({(input) => 
    object next { 
    def apply(y:Int):Int = ...body... 
    } 
    next(input) 
}) 

He tolerado aplicación macro de orientación de Scala 2.11 (con pequeño ajuste también debe trabajar con 2.10) en this gist.

Con esta macro, podemos realizar tareas recursivas ordinarias de forma anónima sin temor al desbordamiento de pila, p.

import asia.blip.ymacro.YMacro._ 
(y[BigInt,BigInt]((xx) => (y) => if(y==1) 1 else y * xx(y-1)))(2000) 

da

res0: BigInt = 33162750924506332411753933805763240382811... 
+0

Bastante impresionante. –

+0

Supongo que aún me pregunto si puede agregar la anotación tailrec para que pueda tener la optimización de la cola de llamadas. –

+0

@JoshCason cree que Scala es lo suficientemente inteligente como para inferir tailrec en el ejemplo que dí. No estoy seguro de casos de uso más complicados para ser honesto. –

1

Un enfoque muy simple:

val fact = { (x: Int) => 
    def f(x: Int): Int = if (x == 0) 1 else x * f(x-1) 
    f(x) 
} 

// Use as anonymous function below 
(1 to 5).map { (x: Int) => 
    def f(x: Int): Int = if (x == 0) 1 else x * f(x-1) 
    f(x) 
} 

// res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 6, 24, 120) 
Cuestiones relacionadas