2016-05-06 10 views
14

ten fragment jest z realizacji Rational Numbers w Julia:Zrozumienie rekursji bez podstawy w przypadku Julii

# Rational.jl 
# ... 
Rational{T<:Integer}(n::T, d::T) = Rational{T}(n,d) 
Rational(n::Integer, d::Integer) = Rational(promote(n,d)...) 
Rational(n::Integer) = Rational(n,one(n)) 

//(x::Rational, y::Integer) = x.num // (x.den*y) <--- HERE! 

# ... 

zobaczyć jak funkcja // jest realizowany, a następnie wykorzystywane przy zapisie Infix? Jak to faktycznie zwraca wartość?

Kiedy zobaczyłem ten kod interpretować to tak:

  1. Funkcja // nazywa z racjonalnego i liczbą całkowitą.
  2. Ale wtedy tworzy rekursywne wywołanie bez żadnych innych argumentów.

# 2 to ten, który naprawdę mnie myli. Gdzie kończy się rekursja w strukturze danych? W jaki sposób // zwraca wartość, jeśli stale ocenia nic?

Proszę, pomóżcie mi to zrozumieć.

Odpowiedz

12

Działa to z powodu jednej z najbardziej podstawowych cech Julii: wielokrotnej wysyłki. W Julii funkcje mogą mieć wiele metod, które mają zastosowanie do różnych kombinacji typów argumentów, a kiedy wywołujesz funkcję, Julia wywołuje najbardziej specyficzną metodę, która pasuje do wszystkich argumentów, które wywołałeś. Wywołanie // w opublikowanej definicji metody definiuje wartość rational-integer // pod względem liczby całkowitej integer // - więc nie jest rekurencyjne, ponieważ metoda nie wywołuje samej siebie, wywołuje inną metodę, która jest częścią tego samego " funkcja ogólna ".

Aby zrozumieć, jak działa wielokrotna wysyłka w tym przypadku, rozważmy ocenę wyrażenia (3//4)//6. Użyjemy makra @which, aby zobaczyć, która metoda wywołuje każde wywołanie funkcji.

julia> @which (3//4)//6 
//(x::Rational{T<:Integer}, y::Integer) at rational.jl:25 

Od 3//4 jest Rational{Int} <: Rational i 6 jest Int <: Integer i zastosowania żadne inne bardziej specyficzne metody, metoda ta nazywa się:

//(x::Rational, y::Integer) = x.num // (x.den*y) 

The current version metody jest rzeczywiście nieco bardziej skomplikowany niż to, co wysłałeś, ponieważ został zmodyfikowany, aby sprawdzić przepełnienie - ale zasadniczo jest taki sam i łatwiej jest zrozumieć starszą, prostszą wersję, więc wykorzystam to. Załóżmy przypisać x i y do argumentów i zobaczyć, jaka metoda nazywa definicja:

julia> x, y = (3//4), 6 
(3//4,6) 

julia> x.num 
3 

julia> x.den*y 
24 

julia> x.num // (x.den*y) 
1//8 

julia> @which x.num // (x.den*y) 
//(n::Integer, d::Integer) at rational.jl:22 

Jak widać, to wyrażenie nie wywołuje ten sam sposób, to nazywa different method:

//(n::Integer, d::Integer) = Rational(n,d) 

Ta metoda po prostu wywołuje konstruktor Rational, który ustawia stosunek n i d na najniższe terminy i tworzy obiekt liczbowy Rational.

Jest dość powszechne, aby zdefiniować jedną metodę funkcji w odniesieniu do innej metody tej samej funkcji, w Julia. Na przykład działają domyślne argumenty.Rozważmy taką definicję:

julia> f(x, y=1) = 2x^y 
f (generic function with 2 methods) 

julia> methods(f) 
# 2 methods for generic function "f": 
f(x) at none:1 
f(x, y) at none:1 

julia> f(1) 
2 

julia> f(2) 
4 

julia> f(2,2) 
8 

Składnia argumentem domyślnym po prostu generuje drugi sposób tylko onee argumentu, który wzywa formę dwóch argument z wartości domyślnej. Więc f(x, y=1) = 2x^y jest dokładnie równoważne określenie dwóch metod, gdzie metoda jednoskładnikowa prostu wywołuje metodę binarnego, dostarczając wartość domyślną dla drugiego argumentu:

julia> f(x, y) = 2x^y 
f (generic function with 1 method) 

julia> f(x) = f(x, 1) 
f (generic function with 2 methods) 
+0

To był bardzo klarowny. Dziękuję Ci. – dopatraman