2017-06-09 10 views
6

Ich habe Funktionen, die Schritte in einem Prozess darstellen. Jede Funktion kennt auch den nächsten Schritt, falls es einen gibt. Ich möchte in der Lage sein, etwas zu tun wie:Ist es möglich, in Kotlin einen rekursiven Funktionstyp zu erstellen?

fun fooStep() : Step? { 
    ... do something ... 
    return ::barStep // the next step is barStep 
} 

Diese Funktionen von einer zentralen Dispatching-Funktion aufgerufen werden, die Code ein bisschen wie folgt enthält:

var step = startStep 
while (step != null) { 
    step = step() 
} 

Beachten Sie, dass die Logik in einem bestimmten Schritt bestimmt auch den nächsten Schritt, wenn es sogar einen gibt.

Ich dachte, ich Step wie definieren könnte: So ein Step eine Funktion ist

typealias Step =() -> Step? 

, die eine andere Step zurückgibt, oder null. Das kompiliert jedoch nicht mit:

Kotlin: Recursive type alias in expansion: Step 

Ich kann umgehen, indem Sie die Funktion in einem Objekt umschließen. zB:

data class StepWrapper(val step:() -> StepWrapper?) 

und meine Funktionssignaturen entsprechend ändern.

Leider bedeutet dies, dass ich nicht nur Funktionsliteralen (zB: ::barStep), sondern wickeln, um sie in einem StepWrapper:

fun fooStep() : StepWrapper? { 
    ... do something ... 
    return StepWrapper(::barStep) 
} 

(Ich habe auch meine Dispatch Schleife ändern sich entsprechend.)

Ich möchte die Notwendigkeit vermeiden, diese Wrapper-Objekte zu erstellen, wenn möglich. Gibt es eine Möglichkeit, dies in Kotlin zu tun?

+1

https://github.com/Kotlin/KEEP/blob/master/proposals/type-aliases.md Nicht diese nach, zumindest nicht – Novaterata

+0

eine mit der Funktion typealias Gibt es einen zwingenden Grund, dass ein Schritt muss den nächsten Schritt wissen? Ein anderer Ansatz wäre, eine Step-Funktion zu haben, die eine Condition zusammen mit einem Graphen von Step-Funktionsreferenzen zurückgibt. Jeder Knoten in dem Graphen enthält die aufzurufende Schrittfunktion und dann eine Zuordnung von dem, was als nächstes aufgerufen werden soll, abhängig von der Bedingung, die zurückgegeben wird. Es würde dann einen Executor geben, der diesen Graphen durchlaufen könnte. – roobyroo

+0

@roobyroo Was ist der Vorteil für den 'Map ' Ansatz gegenüber dem in der Frage beschriebenen 'StepWrapper' Ansatz? –

Antwort

1

Sie können definieren, indem Sie eine generische Schnittstelle:

interface StepW<out T> :()->T? 

interface Step : StepW<Step> 


class Step1 : Step { 
    override fun invoke(): Step? = Step2() 
} 

class Step2 : Step { 
    override fun invoke(): Step? = null 
} 

Wo Step Ihre rekursive Funktion Typ ist.

+0

Interessant! Ich wusste nicht, dass Kotlin Ihnen erlauben würde, einen Funktionstyp mit einer Schnittstelle zu erweitern. Dies löst zumindest das Wrapper-Objekt-Problem, obwohl es zu schade ist, dass eine Klasse notwendig ist, um diese Schnittstelle tatsächlich zu implementieren. –

0

Hier ist, wie Sie es funktioniert, obwohl ich wirklich bin nicht sicher, was Sie versuchen, mit ihm zu erreichen:

typealias Fun<T> =() -> T 
typealias Step<T> =() -> (T) 

typealias Step1 = Step<Fun<Step2>> 
typealias Step2 = Step<Fun<Step3>> 
typealias Step3 = Step<Unit> 

fun step1(): Step1 { 
    return { 
     println("step 1") 
     ::step2 
    } 
} 

fun step2(): Step2 { 
    return { 
     println("step 2") 
     ::step3 
    } 
} 

fun step3(): Step3 { 
    return { println("done") } 
} 
+0

Dies würde nicht funktionieren, da ich alle Schritte benötigen, um den gleichen Typ zu haben. Außerdem hat ein gegebener Schritt nicht immer den gleichen folgenden Schritt oder die gleiche Anzahl aufeinanderfolgender Schritte (abhängig von der Eingabe), aber dieser Ansatz codiert die gesamte Länge des Prozesses in dem Typ. –

0

Verwenden Sie ein Enum den Zustand Muster mit endlichen Zuständen zu implementieren und zu bevorzugen Rückgabe von Nicht-Null-Werten Eine Enumeration kann von einer Funktion erben.

enum class Step :() -> Step { 
    Step1 { 
     override fun invoke() = Step2 
    }, 
    Step2 { 
     override fun invoke() = End 
    }, 
    End { 
     override fun invoke() = this 
    } 
} 

fun work() { 
    var step = Step.Step1 
    while (step !== Step.End) { 
     step = step() 
    } 
} 
Verwandte Themen