2008-10-18 12 views
11

Obecnie pracuję nad projektem z Haskellem i znalazłem trochę problemów. Mam przeczytać i wstawić do listy każdą linię w pliku "dictionary.txt", ale nie mogę tego zrobić. Mam ten kod:Haskell: Wstawianie każdej linii z pliku do listy

main = do 
    let list = [] 
    loadNums "dictionary.txt" list 

loadNums location list = do 
    inh <- openFile location ReadMode 
    mainloop inh list 
    hClose inh 

mainloop inh list = do 
    ineof <- hIsEOF inh 
    if ineof 
     then return() 
     else do 
      inpStr <- hGetLine inh 
      inpStr:list 
      mainloop inh list 

to ma dostać każdy wiersz (wiem, że to robi się każdy wiersz, ponieważ zastąpienie „inpStr: lista” z „putStrLn inpStr” działa prawidłowo, wyświetlając wszystkie linie), a następnie włóż go do listy, ale pojawia się następujący błąd:

Couldn't match expected type `IO' against inferred type `[]' 

Prawdopodobnie dlatego, że hGetLine nie jest ciągiem znaków, ale IO String, które nie mam pojęcia, jak radzić sobie w celu uzyskania odpowiedni ciąg, który mogę wstawić na mojej liście. Nie mam pojęcia, jak można to rozwiązać, lub co to jest dokładnie, ale jeśli ktoś ma jakiś pomysł, jak poprawnie umieścić każdą linię w pliku, byłbym wdzięczny.

Z góry dziękuję!

Odpowiedz

14

W linii, w której wystąpił błąd, Haskell oczekuje "IO a", ale dajesz mu []. Upraszczając wiele rzeczy, na bloku do monady IO, każda linia to:

  • Coś, co zwraca wartość typu "IO a"; wartość typu "a" w nim jest odrzucana (więc "a" jest często "()")
  • A < - wyrażenie, które robi to samo, ale zamiast odrzucać wartość typu "a" nadaje mu nazwę na lewo od < -
  • zawód, który nie robi nic więcej niż nadać nazwę wartości

W tym zrobić blokiem, przy „hGetLine inh” zwraca „Io string” , a ciąg w nim jest wyodrębniany i otrzymuje nazwę inpStr. Następna linia, ponieważ nie jest ani let, ani < - powinna mieć typ "IO a", którego nie zawiera (powodując błąd kompilatora). Co można zrobić, zamiast, ponieważ masz już napisu, to let:

let list' = inpStr:list 

Tworzy nową listę składającą się z ciągu znaków, a następnie na pierwotnej liście, i nadaje mu nazwę „liście””.

Zmień następującą linię, aby użyć "listy" zamiast "listy" (w ten sposób podając nową listę). Ten wiersz wywołuje (rekursywnie) pętlę główną, która odczytuje jeszcze jedną linię, wywołuje samą siebie i tak dalej. Po przeczytaniu całego pliku, zwróci coś za pomocą typu "IO()". To "IO()" zostanie zwrócone do bloku "do" na loadNums. Gratulacje, właśnie utworzyłeś listę z liniami odczytanymi z pliku, w odwrotnej kolejności (od kiedy dołączyłeś do nagłówka listy), a potem nic nie zrobiłeś.

Jeśli chcesz coś z tym zrobić, zmień "return()" na "return list"; return wygeneruje wartość typu "IO [String]", z zawartą w nim listą (return nie zawiera nic poza enkapsulacją wartości), którą można wyodrębnić w loadNums przy użyciu składni <.

Resztę pozostawia się czytelnikowi jako ćwiczenie.

15

Jeśli nie jest to praca domowa lub coś w tym stylu, nie ma powodu, aby używać tak dużo wysiłku. Ponowne użycie jest leniwy!

getLines = liftM lines . readFile 

main = do 
    list <- getLines "dictionary.txt" 
    mapM_ putStrLn list 

Ale jak to wydaje się być jeszcze nauka Haskell, ważne jest, aby zrozumieć, co CesarB napisał.

+0

Wyjaśniając rzeczy komuś, kto wydaje się nadal uczyć Haskella, unikałbym używania (a nawet pokazywania) bezsensownego stylu. Nie chcę ich przestraszyć ;-) – CesarB

+7

+1 za wymienienie 'readFile'. Pomocne jest wyjście z imperatywnej postawy otwartego pliku/odczytu linii/wykrycia pliku EOF/close, a funkcje takie jak "readFile", "getContents' i" interact "poradzą sobie z bałaganem. – Nefrubyr

+0

Co oznacza "mapM_"? Wiem, że "mapM" mapuje funkcję monady ... ale tę z podkreśleniem? – drozzy

Powiązane problemy