2015-06-12 14 views
11

Po pierwsze, przepraszam za tytuł nieopisujący. Ponieważ nie mam pojęcia, co się właściwie dzieje, nie mogę zrobić tego bardziej konkretnego.Dlaczego GHCI "zatrzymuje się" w stanie błędu po błędzie?

Teraz na moje pytanie. I wprowadziły następujące fragment do problemu 23 99 Haskell problems, które należy losowo wybrać n pozycji z listy:

rndSelect' :: RandomGen g => [a] -> Int -> g -> ([a], g) 
rndSelect' _ 0 gen = ([], gen) 
rndSelect' [] _ _ = error "Number of items requested is larger than list" 
rndSelect' xs n gen = ((xs !! i) : rest, gen'') 
        where (i, gen') = randomR (0, length xs - 1) gen 
          (rest, gen'') = (rndSelect' (removeAt xs i) (n - 1) gen') 

rndSelectIO' :: [a] -> Int -> IO [a] 
rndSelectIO' xs n = getStdRandom $ rndSelect' xs n 

removeAt :: [a] -> Int -> [a] 
removeAt xs n 
    | length xs <= n || n < 0 = error "Index out of bounds" 
    | otherwise = let (ys, zs) = splitAt n xs 
        in ys ++ (tail zs) 

Teraz kiedy załadować to w ghci to działa poprawnie z ważnych argumentów:

*Main> rndSelectIO' "asdf" 2 >>= putStrLn 
af 

jednak dziwne rzeczy dzieją się, gdy używany jest indeks, który jest poza zakresem:

*Main> rndSelectIO' "asdf" 5 >>= putStrLn 
dfas*** Exception: Number of items requested is larger than list 
*Main> rndSelectIO' "asdf" 2 >>= putStrLn 
*** Exception: Number of items requested is larger than list 

Jak widać, po 2 (dla mnie) zdarzają się niespodziewane rzeczy:

  1. Zamiast bezpośrednio podawać błąd, najpierw drukuje permutację wejścia.
  2. Po tym, jak raz wystąpił błąd, nie będzie już więcej wykonywany.

Podejrzewam, że 1. ma związek z leniwą oceną, ale absolutnie nie mam pojęcia, dlaczego 2. się dzieje. Co tu się dzieje?

+0

Każda linia w sesji interaktywnej znajduje się wewnątrz tego samego niejawnego wyrażenia 'do', które nigdy się nie kończy, więc każda linia przekazuje swój wynik do następnego. – chepner

Odpowiedz

13

Funkcjaw zasadzie wyszukuje wartość StdGen w zmiennej globalnej, uruchamia na niej pewną funkcję, umieszcza nowy materiał siewny z powrotem w zmiennej globalnej i zwraca wynik do osoby wywołującej.

Jeśli dana funkcja wraca z błędem, błąd ten zostaje wprowadzony do zmiennej globalnej. Teraz wszystkie próby użycia tej zmiennej globalnej rzucają wyjątek. (I powiedział zmienne globalne są złe! ;-))

Spróbuj samodzielnie wywołać getStdGen. To albo wydrukuje bieżący losowy materiał siewny, albo rzuci wyjątek. Jeśli zgłasza wyjątek ... jest twój problem.

Wierzę, że można użyć setStdGen, aby zresetować rzecz.

+0

Czy to problem z lenistwem? 'getStdRandom' nie powinno być w stanie przywrócić błędu w' StdGen'. – mariop

+0

@mariop Prawdopodobnie dopuszczenie pojedynczej zmiennej globalnej jest w pierwszej kolejności hakerem (np. Zagrożenie bezpieczeństwa wątku może być zagrożone). Ale tak, myślę, że mógłbyś twierdzić, że powinien zrobić coś w stylu hoopy, aby uniknąć ponownego wstawienia wyjątku do globalnej zmiennej ... – MathematicalOrchid

+0

Jak to się dzieje w aplikacjach świata rzeczywistego? Czy wszyscy inicjują swój własny generator losowy przez cały czas? Czy jest to zamierzona decyzja projektowa, czy też był to nadzór, aby nie łapać błędów w 'getStdRandom'? – Tiddo

Powiązane problemy