Ich habe zwei Listen in Haskell.

Ursprüngliche Liste mit Zeichenfolgenwerten:

["Hello", "HELLO", "", "WORLD", "xxx", "world"]

Indexliste mit ganzzahligen Werten, wobei die Zeichenfolgen in der ursprünglichen Liste alle in Großbuchstaben geschrieben sind:

[1,3]

Ich habe alle Werte in der Indexliste mit einer von mir erstellten Funktion erhöht und eine index2 Liste erstellt. Insgesamt sieht es so aus:

Mein Code:

import Data.List 
import Data.Char 
import Data.Maybe 

main = do 
    contents <- readFile "h.txt" 
    let original = lines (contents) 
    let allUpper = lines (map toUpper contents) 
    let onlyUpper = filter(/="") (intersect original allUpper) 
    let upperIndex = findIndices ('elem' onlyUpper) original 
    let index2 = (map increment upperIndex) 
    print index2 

increment :: Int -> Int 
increment x = x+1 

Ich habe es geschafft, mit deiner Hilfe so weit zu kommen. Da ich jedoch Anfänger bin, scheine ich nicht zu verstehen, wie die Iteration über Listen funktioniert.

Ich möchte überprüfen, ob die entsprechenden Indexwerte (in index2) in der ursprünglichen Liste leer sind oder nicht. Wenn sie leer sind, möchte ich sie in index2 entfernen.

-3
Orkun 27 Dez. 2015 im 07:15

2 Antworten

Beste Antwort

Leere Elemente filtern

Ich möchte überprüfen, ob die entsprechenden Indexwerte (in Index2) in der ursprünglichen Liste leer sind oder nicht. Wenn sie leer sind, möchte ich sie in Index2 entfernen.

Der Code filtert bereits leere Elemente heraus! Schauen Sie sich folgende Zeile an:

let onlyUpper = filter(/="") (intersect original allUpper)

Diese Zeile macht zwei Dinge:

  • Es werden nur Elemente beibehalten, die nur aus Großbuchstaben (intersect original allUpper) bestehen.
  • Es filtert leere Elemente heraus (filter(/="")).

Wenn Sie mit leeren Elementen Zeichenfolgen meinen, die nur Leerzeichen oder nichts enthalten, können Sie stattdessen Folgendes verwenden:

filter (all isSpace)

Durch Listen iterieren

Ich scheine nicht zu verstehen, wie die Iteration über Listen funktioniert.

In Haskell sind Listen einfach verkettete Listen: Jedes Element enthält einen Wert und einen Verweis auf den nächsten Wert.

Daher werden Listen nicht indiziert: Der Operator !! muss jedes Element durchlaufen, um auf ein bestimmtes Element zuzugreifen, wodurch Listen beim direkten Zugriff völlig ineffizient werden.

Wenn Sie eine Liste an eine Funktion senden, geben Sie ihr einfach das erste Element.

Mit diesen Überlegungen müssen Sie beim Bearbeiten von Listen vermeiden, über ihren Index auf Elemente zuzugreifen.

Die Idee ist, Funktionen zu erstellen, die ihre Arbeit mit einfachen Werten erledigen und sie einer Liste von Elementen zuordnen. Werfen Sie einen Blick auf die Funktion toUpper:

toUpper :: Char -> Char

Es nimmt ein Char und gibt seine Großbuchstabenversion zurück (auch ein Char).

Haskell hat keine toUpper Funktion, die auf String funktioniert. Sie müssen etwas wie map oder <$> verwenden, um toUpper auf eine Liste von Zeichen anzuwenden ( a String):

map toUpper "ab" -- "AB"
toUpper <$> "ab" -- "AB"

Die Idee ist, Funktionen zu haben, die nur eine bestimmte Sache tun. Das Aufrufen und Durchlaufen einer Liste sind zwei verschiedene Dinge. Muss die Funktion toUpper den Index des Elements kennen, das in Großbuchstaben geschrieben wird? Nein!

Durchlaufen einer Liste mit Index

Sie fragen sich vielleicht: Aber was ist, wenn meine Funktion den Index der Elemente WIRKLICH berücksichtigen muss? (dh zum Herausfiltern von geraden oder ungeraden Elementen).

Sie haben zwei Möglichkeiten, dies zu berücksichtigen:

  • a List ist nicht der Typ, mit dem Sie arbeiten müssen. Möglicherweise sind Data.Map, Data.IntMap oder Data.Vector besser für die Aufgabe geeignet (weitere Informationen finden Sie in diesen Modulen).
  • Sie müssen einen Zwischentyp verwenden, der den Index enthält.

Beispielsweise:

let string = "abcde"
let indexedString = zip [1..] string

print indexedString -- [(1, 'a'), (2, 'b), (3, 'c), (4, 'd), (5, 'e)]

Beachten Sie, dass dies auch Ihre Notwendigkeit einer Inkrementierungsfunktion löst, da der Index mit einem beliebigen Wert gestartet wird.

Um zur ursprünglichen Zeichenfolge zurückzukehren, schreiben Sie:

map snd indexedString -- "abcde"

Sie müssen die Funktionen fst und snd verwenden, um mit dem Zwischentyp zu arbeiten, oder um den Mustervergleich zu verwenden:

filter (\x -> snd x == 'b') indexedString -- [(2, 'b')]
map (\(i,s) -> (i, toUpper s)) indexedString -- [(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E')]

Berücksichtigung des Index:

let string = "abcde"
    indexedString = zip [1..] string

    upperEven (i, c) | even i = (i, toUpper c)
                     | otherwise = (i, c)

print $ map upperEven indexedString -- [(1,'a'),(2,'B'),(3,'c'),(4,'D'),(5,'e')]
print $ map snd $ map upperEven indexedString -- "aBcDe"

Anmerkungen

Die increment -Funktion existiert bereits in Haskell und heißt succ (es ist auch eine allgemeinere Funktion, die für alle Typen funktioniert, die die Enum -Klasse wie Int, Char ... unterstützen.)

2
zigazou 27 Dez. 2015 im 07:08

Warum nicht words :: String -> [String] für den Inhalt verwenden, den Sie aus einer Datei erhalten? Die Verwendung von lines :: String -> [String] wäre eine Alternative, wenn man ein Wort pro Zeile hatte.

Wenn ich Ihr Problem richtig verstehe, können Sie Folgendes schreiben, um Ihr Problem zu lösen:

import Data.List (findIndices)
import Data.Char (isUpper)

allUpperOneBasedIndices :: String -> [Int]
allUpperOneBasedIndices = map succ . findIndices (all isUpper) . words
0
denys.fridman 27 Dez. 2015 im 07:53