2015-05-29 9 views
14

Swift'deki global işlevlerle özyineleme önemsizdir. Örneğin, bir kapanma kendisine atıfta bulunamaz. Bununla birlikte, bir kapanma kendisine atıfta bulunamaz. Örneğin:Swift'de nasıl bir _inline_ özyinelemeli kapatma oluşturabilirim?

Variable used within its own initial value 

bir çözüm bu var mı:

var f: (Void -> Void) = 
{ 
    f() 
} 

şu hatayı verir? satır içi özyinelemeli bir kapatma nasıl oluşturabilirim?

func unimplemented<T>() -> T 
{ 
    fatalError() 
} 

func recursive<T, U>(f: (@escaping (((T) -> U), T) -> U)) -> ((T) -> U) 
{ 
    var g: ((T) -> U) = { _ in unimplemented() } 

    g = { f(g, $0) } 

    return g 
} 

recursive((T) -> U) kapağın bir versiyonun bir başvurudur bir kapak (((T) -> U), T) -> U alır ve kullanışlı bir fonksiyonu g döner bir fonksiyonudur:

cevap

3

bir çözüm yoktur. İlk olarak bir sahte işlev atanır (çağrı üzerine çöker). Bu, g'un g'un yeni değeri için yinelemeyi sağlamak için yapılır, buradagiriş değeriyle birlikte f'a geçirilir. g = { f(g, $0) }'daki g'un kendisine atıfta bulunan sahte işleve değil, kendisine atıfta bulunduğunu belirtmek önemlidir. Bu nedenle, ((T) -> U) parametresi f'da başvurulan her seferinde, g referansıdır;

Bu işlev, aşağıda gösterildiği gibi içi yineleme sağlar:

recursive { f, x in x != 10 ? f(x + 1) : "success" }(0) 

Bu fonksiyon, tek bir değişken bildirmek için gerek kalmadan, 11 kez toplam tekrarlanır.

Güncelleme: Bu şimdi Swift 3 önizleme 6 ile çalışır!


Şahsen burada bir an için konuşmak gerekirse, bunu yürütmem için kodumu kolaylaştırır düşündüğümüzden, şu gayet iyi bir çözüm olarak görüyorum. Böyle

func recursive<T, U>(_ f: (@escaping (@escaping (T) -> U) -> ((T) -> U))) -> ((T) -> U) 
{ 
    return { x in return f(recursive(f))(x) } 
} 

aşağıda biri olarak bir Y combinator yaklaşım, şundan bir kaçış Kapak içindeki bir fonksiyon, bir kaçan kapatılmasını dönmek zorunda kalacak!

recursive { f in { x in x != 10 ? f(x + 1) : "success" } }(0) 

Yukarıdaki kod iç @escaping öznitelik için değilse geçersiz sayılacak. Aynı zamanda satır içi kod yazarken, rahat olduğumdan daha ayrıntılı görünmesini sağlayan başka bir dizi parantez gerektirir.

+0

Bu nasıl yukarıda Kirsteins' cevabını daha iyi değilsiniz? Daha fazla boilerplate eklediniz ve yinelemeli fonksiyonunuzun '(T -> U)' şeklinde olduğunu varsayıyorsunuz (yani, eğer durum böyle değilse, tip sistemiyle akrobasi yapmak zorundasınız). İdeal bir çözüm olmasa da, örtük olarak isteğe bağlı olan bu dil için isteğe bağlı isteğe bağlı diller tanıtıldı. –

+0

@SashaChedygov: Zaman damgalarına bakarsanız, bunun sorunun ilk cevabı olduğunu görebilirsiniz. Amacım, bilgimi bir Q/A biçiminde paylaşmaktı. –

+0

Yeterince adil, zaman damgalarını yanlış okuyorum. –

13

Kısıtlama, her biri için aynı anda iki nesne yapılamaz. Birinin diğerinden yaratılması gerekiyor. İstenmeyen isteğe bağlı olarak örtülü işlevi işaretleyebilirsiniz. Bu şekilde, nil ile işlevi başlatırsınız, ancak daha sonra değere sahip olacağına "vaat" edersiniz.

var f: (Void -> Void)! 

f = { 
    f() 
} 

Güncelleme: başka bir yaklaşım olabilir:

var f: (Void -> Void) 

var placeholder: (Void -> Void) = { 
    f() 
} 

f = placeholder 
+0

Bu işe yaramasa da, çirkin ve yapmamayı tercih ettiğim opsiyonelleri kullanmamı gerektiriyor. –

+1

İnanacağım başka bir seçenek yok. Aynı yaklaşım, @ IBOutlet için kullanılır. Bu isteğe bağlı dillerin örtük olarak kapatılmasının ana sebebi olduğuna inanıyorum. – Kirsteins

+0

Kendi yanıtıma bir bakın. –

İlgili konular