2015-07-14 9 views
5

Czy używałbyś/else do napisania tego algorytmu w Haskell? Czy istnieje sposób, aby wyrazić to bez nich? Trudno wyodrębnić funkcje ze środka, które mają znaczenie. To tylko wynik systemu uczenia maszynowego.Jak wyrazicie to w Haskell?

Wprowadzam algorytm klasyfikacji segmentów treści html jako Content lub Boilerplate opisany here. To ma ciężary już zakodowane.

curr_linkDensity <= 0.333333 
| prev_linkDensity <= 0.555556 
| | curr_numWords <= 16 
| | | next_numWords <= 15 
| | | | prev_numWords <= 4: BOILERPLATE 
| | | | prev_numWords > 4: CONTENT 
| | | next_numWords > 15: CONTENT 
| | curr_numWords > 16: CONTENT 
| prev_linkDensity > 0.555556 
| | curr_numWords <= 40 
| | | next_numWords <= 17: BOILERPLATE 
| | | next_numWords > 17: CONTENT 
| | curr_numWords > 40: CONTENT 
curr_linkDensity > 0.333333: BOILERPLATE 

Odpowiedz

6

Ponieważ istnieją tylko trzy ścieżki w tym drzewa decyzyjnego, która prowadzi do stanu Gotowa, to bym po prostu iteracyjne i uprościć je:

isBoilerplate = 
    prev_linkDensity <= 0.555556 && curr_numWords <= 16 && prev_numWords <= 4 
    || prev_linkDensity > 0.555556 && curr_numWords <= 40 && next_numWords <= 17 
    || curr_linkDensity > 0.333333 
+4

myślę chcesz 'or', nie' any'. Możesz też użyć '||' zamiast lub 'i' zamiast '&&' s. –

11

Nie uproszczenie logiki ręcznie (zakładając, że można generować ten kod automatycznie), myślę, że używanie MultiWayIf jest całkiem czyste i bezpośrednie.

{-# LANGUAGE MultiWayIf #-} 

data Stats = Stats { 
    curr_linkDensity :: Double, 
    prev_linkDensity :: Double, 
    ... 
} 

data Classification = Content | Boilerplate 

classify :: Stats -> Classification 
classify s = if 
    | curr_linkDensity s <= 0.333333 -> if 
     | prev_linkDensity s <= 0.555556 -> if 
     | curr_numWords s <= 16 -> if 
      | next_numWords s <= 15 -> if 
      | prev_numWords s <= 4 -> Boilerplate 
      | prev_numWords s > 4 -> Content 
      | next_numWords s > 16 -> Content 
     ... 

i tak dalej.

Jednak skoro jest to tak uporządkowane - po prostu drzewo wyrażenia if/else z porównaniami, należy również rozważyć utworzenie struktury danych drzewa decyzyjnego i napisanie dla niego interpretera. Umożliwi to dokonywanie transformacji, manipulacji, inspekcji. Może ci coś kupi; określenie miniaturowych języków dla twoich specyfikacji może być zaskakująco korzystne.

data DecisionTree i o 
    = Comparison (i -> Double) Double (DecisionTree i o) (DecisionTree i o) 
    | Leaf o 

runDecisionTree :: DecisionTree i o -> i -> o 
runDecisionTree (Comparison f v ifLess ifGreater) i 
    | f i <= v = runDecisionTree ifLess i 
    | otherwise = runDecisionTree ifGreater i 
runDecisionTree (Leaf o) = o 

-- DecisionTree is an encoding of a function, and you can write 
-- Functor, Applicative, and Monad instances! 

Następnie

classifier :: DecisionTree Stats Classification 
classifier = 
    Comparison curr_linkDensity 0.333333 
     (Comparison prev_linkDensity 0.555556 
     (Comparison curr_numWords 16 
      (Comparison next_numWords 15 
      (Comparison prev_numWords 4 
       (Leaf Boilerplate) 
       (Leaf Content)) 
      (Leaf Content) 
      ... 
+2

Dla pierwszej metody rozszerzenie 'MultiWayIf' pozwala ci napisać ładniej, zastępując' case() z _' przez 'if'. –

+0

@ ØrjanJohansen, to jest miłe, zaktualizowane. :-) – luqui

+3

Niewielka uwaga: mam osobistą niesmak dla strażników, które mają na celu objęcie wszystkich spraw (tj. Wyczerpujące) i nie używają "inaczej" dla ostatniego strażnika. Używam "inaczej", ponieważ uważam, że lepiej wyraża to intencje, nie muszę powtarzać warunku dwa razy i jest bardziej efektywny niż cokolwiek innego. Ponadto, gdy wewnętrzne "if" ma niewyczerpujące gałęzie, bardzo łatwo (przynajmniej dla mnie) myśleć, że Haskell wycofa się do następnej zewnętrznej gałęzi 'if'. Tak jednak nie jest, i powstaje niewyczerpujący błąd środowiska wykonawczego. Z tych powodów zdecydowanie preferuję ostatnią alternatywę: – chi