2011-12-30 9 views
9

Jakie są pomysły na wyrażanie tej funkcji w Scence "idiomatycznej". A dokładniej, czy istnieje sposób na usunięcie lokalnych więzów bez utraty czytelności?Rozwiązanie Idiomatic Scala do imperatywnego kodu

def solve(threshold: Int)(f: Int => Int): Int = { 
    var sum = 0 
    var curr = 0 
    while(sum < threshold) { 
    sum += f(curr) 
    curr += 1 
    } 
    curr 
} 

Jedyne, co mogłem wymyślić, to, ale jest dłuższa i mniej czytelna według mnie.

def solve2(threshold: Int)(f: Int => Int): Int = { 
    val resultIterator = Iterator.iterate (0, 0) { case (curr, sum) => 
    (curr + 1, sum + f(curr)) 
    } 
    (resultIterator find (_._2 >= threshold)).get._1 
} 
+0

Trudno było zdecydować, który z nich zrobić _correct_ ponieważ wszystkie były dobre, więc wybrałem ten, który wydawał się najbardziej intuicyjna me –

+0

@Dan Rozwiązanie Burtona dało mi najwięcej nowych sztuczek dla skrzynki narzędziowej. –

Odpowiedz

10

Najbardziej bezpośrednim podejściem jest przekształcenie pętli while w zagnieżdżoną funkcję rekurencyjną.

def solve(threshold: Int)(f: Int => Int): Int = { 
    def solveLoop(sum: Int, curr: Int): Int = if (sum < threshold) { 
     solveLoop(sum + f(curr), curr + 1) 
    } else { 
     curr 
    } 
    solveLoop(0,0) 
} 

Jest to standardowy "funkcjonalny" sposób zapętlania.

+0

Po odbiciu jest to po prostu nierozciągnięta wersja mojego "do zrobienia". :) –

13
def solve(threshold: Int)(f: Int => Int): Int = { 
    Iterator.from(0).map(f).scanLeft(0)(_ + _).indexWhere(threshold <=) 
} 

Moim zdaniem wersja z pętlą jest znacznie bardziej przejrzysta.

7

mógł

def solve(threshold: Int, i: Int = 0)(f: Int => Int) = { 
    if (threshold <= 0) i else solve(threshold - f(i), i+1)(f) 
} 

ale nie jestem pewien, że to faktycznie jaśniej Ty. Należy pamiętać, że jest to rzeczywiście więcej znaków niż kompaktowej wersji pętli while:

def solve(threshold: Int)(f: Int => Int) = { 
    var s,i = 0; while (s < threshold) { s += f(i); i += 1 }; i 
} 

Pętle z modyfikowalnych zmiennych nie zawsze są złe, „idiomatyczne” lub nie. Po prostu zachowaj bezpieczny stan zawarty w tej funkcji, a każdy, kto widzi, jest funkcją bezpaństwową.

Nawiasem mówiąc, chociaż sum jest rozsądną nazwą zmiennej, to wątpliwe jest, czy jest to curr. Co jest nie tak z i? Jest szeroko stosowany jako zmienna indeksowa, a zresztą posiadanie zmiennej w ogóle jest uciążliwe; chodzi o to, że bierzesz coś i zwiększasz, cokolwiek to jest, krok po kroku za każdym razem, a potem zwracasz. To właśnie ten przepływ logiki, a nie nazwa, mówi wam (i innym), do czego to służy.

+0

Zauważyłem też, że "curr" rozprasza. – huynhjl

+0

Masz rację co do nazwy zmiennej. Mogę tylko powiedzieć, że w oryginalnym kontekście nazwa miała trochę więcej sensu. –

1

Zawsze się zastanawiam, kiedy ludzie mówią o "idiomatycznej" scali. Ponieważ moim zdaniem każdy ma własną percepcję idiomatyczną. Jeśli szukasz funkcjonalnego rozwiązania, chciałbym zaproponować ci spojrzenie na "istotę wzoru iteratora". Tu jest rzeczywiście bardzo dobry blogpost w Scala na ten temat to sprawdzić tutaj: http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

+0

Czy możesz pokazać, z jakim kodem jest tutaj "traverse"? – missingfaktor

+0

Cóż, jeśli spojrzeć na: próg, sumę i cur. Co on robi z tymi wartościami? Używa ich do generowania skończonego "strumienia" wartości, którym on sam stosuje swoją funkcję. Przekonany?:) – AndreasScheinert

+0

Cóż, jeśli spojrzeć na: próg, sumę i cur. Co on robi z tymi wartościami? Używa ich do generowania skończonego "strumienia" wartości, którym on sam stosuje swoją funkcję. Przekonany? Właściwie bardziej podoba mi się implementacja scanLeft. Idiomatyczny jest bardzo subiektywny, niektórzy ludzie mają na myśli zwięzłość lub coś innego. Generowanie sekwencji i stosowanie funkcji w stanie przewlekania. To zależy od tego, jak chcesz to wyrazić. – AndreasScheinert

4

Oto w jaki sposób mogę to zrobić w Haskell:

solve threshold f = snd $ until test step (0, 0) 
    where test (sum, i) = sum >= threshold 
     step (sum, i) = (sum + f i, succ i) 

To wyraźnie zaznacza test The step, a początkowy wartości, podobnie jak wersja imperatywna. Nie jestem pewien, czy Scala ma until w libs gdzieś, ale to jest trywialne, aby zdefiniować:

def until[A](test: A => Boolean)(f: A => A)(v: A): A = { 
    if (test(v)) { 
    v 
    } else { 
    until(test)(f)(f(v)) 
    } 
} 

def solve(threshold: Int)(f: Int => Int): Int = { 
    def test = (sum: Int, i: Int) => sum >= threshold 
    def step = (sum: Int, i: Int) => (sum + f(i), i + 1) 
    until(test.tupled)(step.tupled)((0, 0))._2 
} 
+2

+1, To wygląda na bardzo przydatną funkcję. Powinieneś dodać odpowiednik Scali do swojej odpowiedzi IMO. – missingfaktor

+0

@missingfaktor done :) Napisałem oryginał ostatniej nocy na telefonie i byłem zbyt leniwy, aby przetestować go w Scali. Nie jestem pewien, czy moje liberalne wykorzystanie tego curry jest zgodne z optymalizacją wywołania ogona Scali, ale cóż mogę powiedzieć, lubię curbowanie Haskella. –

Powiązane problemy