2015-09-17 12 views
9

Numpy ma wyrafinowaną funkcję indeksowania/krojenia/stopniowania w swoim operatorze dostępu do macierzy. Zobacz: http://docs.scipy.org/doc/numpy/reference/arrays.indexing.htmlZaawansowane indeksowanie/dzielenie Numpy'ego w Haskell

Eksperymentując z Haskellem, pomyślałem, że będzie to edukacyjne, aby spróbować replikować podzbiór tej funkcji indeksowania. Konkretnie to „obiekty do wyboru krotka” lub n-wymiarowa projekcja”(https://en.wikipedia.org/wiki/Projection_%28relational_algebra%29)

Zasadniczo można zrobić.

two_d_array[0:1:1, 0:4:2] 

A to daje pierwszy wiersz schodkowy przez 1 zawierający pierwsze 2 kolumny wkroczył przez 2 (pomijam 1 kolumna).

w kolejności słów może wystawać oryginalną 2 wymiarową tablicę do mniejszego 2 wymiarowej tablicy. w rezultacie pozostaje jako 2-wymiarowej tablicy.

Więc oto co próbowałem w Haskell

Rodzaj takiej funkcji powinno być coś takiego:

(!!!) :: (Functor f) => f a -> [(Int, Int, Int)] -> f a 

Więc można zobaczyć coś takiego:

three_d_tensor !!! [(s1,e1,st1), (s2,e2,st2), (s3,e3,st3)] 

Gdzie sx, ex, STX to początek, koniec, krok odpowiednio.

Przykład powinien wystawać oryginalny tensor do mniejszego tensora, z pierwszym wymiarze ograniczonym przez s1 to e1, stepping by st1, drugim wymiarze ograniczonym przez s2 to e2, stepping by st2 ... itd

Więc oto co mam:

slicing from to xs = take (to - from + 1) (drop from xs) 

stepping n = map head . takeWhile (not . null) . iterate (drop n) 

(!!!) tensor ((start, end, step):tail) = 
    project (stepSlice start end step tensor) tail map 
    where 
     project tensor ((start, end, step):tail) projection = 
      project (projection (stepSlice start end step) tensor) tail (projection . map) 
     project tensor [] _ = 
      tensor 
     stepSlice start end step tensor = 
      ((stepping step) . (slicing start end)) tensor 

Powyższe nie działa z powodu problemu "polimorficznej rekursji". Zasadniczo nie mogę nieskończenie komponować funkcji map, specyficzne wyrażenie, które to robi jest (projection . map). Gdyby możliwy był ten rodzaj rekombinacji polimorficznej, uważam, że zadziała. Jestem jednak otwarty na alternatywne implementacje, które nie wymagają polimorficznej rekursji.

ja zbadali ten problem i wciąż wymyślić krótkie:

+0

_Tensors nie są functors_ (zagnieżdżone lub w inny sposób). Tak, czasami jest to coś w rodzaju udawania, i robi to [linear] (http://hackage.haskell.org/package/linear), ale jest to jedna rzecz, w której EKMETT się pomylił, IMO. - Czy naprawdę potrzebujesz rangi zmiennej runtime? – leftaroundabout

+1

Pamiętaj, że ** możesz ** używać polimorficznej rekursji ... pod warunkiem, że 1) typ, który funkcja będzie miała, jest w rzeczywistości możliwy do wyrażenia syntaktycznego i 2) podajesz jawny podpis typu. – Bakuriu

+0

Nie wiem, czy któryś z nich dotyczy, ale wydaje się całkiem interesujący: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#generalised-list-comprehensions i https://hackage.haskell.org/package/DSH – dfeuer

Odpowiedz

6

Jest już typ obliczania nowych wartości z istniejących wartości - funkcje . Zakładając, że mamy funkcję indeksującą do struktury, możemy jej użyć do zindeksowania struktury poprzez zastosowanie jej do struktury.

(!!!) = flip ($) 

infixr 2 !!! 

Jeśli mamy funkcję indeksy struktura i funkcja drugiego indeksy zagnieżdżone budowle można je komponować razem przez fmap ing drugą funkcję nad strukturą następnie zastosowanie funkcji zewnętrznej.

(!.) :: Functor f => (f b -> g c) -> (a -> b) -> f a -> g c 
t !. f = t . fmap f 

infixr 5 !. 

na przykładzie 3d struktury

three_d_tensor :: [[[(Int,Int,Int)]]] 
three_d_tensor = [[[(x, y, z) | z <- [0..4]] | y <- [0..3]] | x <- [0..2]] 

Możemy patrzeć skomplikowany kawałek od zbudowana ze swoimi funkcjami skalowania slicing i stepping.

example = three_d_tensor !!! slicing 1 2 !. stepping 2 !. stepping 2 . slicing 1 3 

co skutkuje

[[[(1,0,1),(1,0,3)],[(1,2,1),(1,2,3)]],[[(2,0,1),(2,0,3)],[(2,2,1),(2,2,3)]]] 
+0

Wow. Działa to naprawdę dobrze. Komponujesz wszystkie rzuty wymiarowe razem "!.", A następnie zastosuj je za jednym razem za pomocą '!!!'. Zauważyłem równoległość z normalną kompozycją '(fb -> fc) -> (fa -> fb) -> fa -> fc' podobną do samej' (.) :: (b -> c) -> (a -> b) -> a -> c'. Ale zamiast tego ten rodzaj "gniazd" kompozycji w treści funktora. Zauważyłem też, że użyłeś 'g', aby umożliwić zmianę samego funktora? Jak nazywa się ten rodzaj transformacji? "Kompozycja zagnieżdżona?" – CMCDragonkai

+0

Główną różnicą było moje pierwotne zamiarem było zrobienie listy prognoz, która ma tę zaletę, że pozwala na arbitralne rzutowanie określone przez pewną wartość runtime. Obecna forma może mieć tylko prognozy określone podczas kompilacji. Ale metaprogramowanie środowiska wykonawczego lub hakery klasy lub typy zależne mogą zezwalać na projekcje oparte na listach. – CMCDragonkai

+0

Możesz konstruować funkcje w czasie wykonywania; to właśnie robi częściowa aplikacja i lambda. Jedyną częścią tego, która musi zostać wykonana w czasie kompilacji, jest sprawdzenie, czy projekcja jest poprawna. – Cirdec

Powiązane problemy