2012-04-07 29 views
1

Mögliche Duplizieren:
Split list and make sum from sublist?Summe der getrennten Elemente in einer Liste

Im Versuch, dieses Problem zu lösen. Ich muss eine Summe von Elementen aus einer Liste machen, die nur mit "0" voneinander getrennt sind. Also zum Beispiel kann ich so etwas als Eingabe haben: [1,2,3,0,3,4,0,2,1] und der Ausgang sollte [6,7,3] sein.

Bisher habe ich es geschafft, so etwas zu tun:

cut (x:xs) | x > 0 = x : (cut xs) 
     | otherwise = [] 

first (xs) = ((foldl (+) 0 (cut   (xs))) ) : [] 
second (xs) = ((foldl (+) 0 (cut (reverse (xs)))) ) : [] 

test (xs) = first(xs) ++ second(xs) 

Problem ist, dass dies nur funktioniert, mit nur 1 Instanz von „0“ in meiner Liste.

Ich habe versucht, diese Funktion und bearbeite meine Schnitt zu lösen:

cut [] = [] 
cut (x:xs) | x > 0 = foldl (+) 0 (x : cut xs) : [] 
     | x == 0 = (cut xs) 

Aber ich kann nicht herausfinden, wie es zu justieren, so wird es die Summen zu trennen. Im Moment wird nur die Summe aller Elemente als Ausgabe ausgegeben.

Antwort

1

Für Hausaufgaben sollten Sie auf jeden Fall die Antwort von dave folgen. Aber hier ist eine erweiterte Lösung, unter Verwendung von groupBy als armen Mannes split:

import Data.List (groupBy) 

map sum $ groupBy (const (/=0)) list 

Dies könnte nett aussehen, aber beachten Sie, dass es immer noch die Nullen am Anfang der Teillisten vorhanden sind, so können Sie‘ t verwenden diese Lösung ohne Änderungen wenn es ankommt (zB wenn Sie Produkte anstelle von Summen benötigen)

[Erklärung]

groupBy sieht, wenn das erste Element der aktuellen Gruppe „zusammen passt“ mit dem aktuellen Element des Liste. In diesem Fall wird das aktuelle Element zur Gruppe hinzugefügt, andernfalls wird eine neue Gruppe gestartet. Z.B.

groupBy (\x y -> x `mod` y == 0) [81,3,9,25,5] 
--[[81,3,9],[25,5]] 

Hier der Test ist erfolgreich für 81 'mod' 3 und 81 'mod' 9, aber nicht für 81 'mod' 25, die eine neue Gruppe beginnt. Auch hier ist 25 'mod' 5 erfolgreich.

Aber in unserem Fall "passen" alle Elemente in die aktuelle Gruppe, solange sie nicht 0 sind, also müssen wir nicht einmal auf das erste Element schauen. Und wenn eine 0 gefunden wird, wird eine neue Gruppe gestartet.

const (/=0) bedeutet nur \_ y -> y /= 0, so egal, was das erste Argument, es prüft nur, dass das zweite Element nicht 0. Um zu sehen ist, warum, Blick auf der Definition:

const :: a -> b -> a 
const a _ = a 

Jetzt kann unser Lambda sein wie

geschrieben
\x y -> const (/= 0) x y 

wie aus dem const Aufruf nur die erste der beiden Argumente „überlebt“ haben wir

\x y -> (/= 0) y 

... oder ...

\_ y -> y /= 0 
+0

Danke, das ist die perfekte Lösung für mich, da ich foldl (+) 0 verwenden muss. Also ich habe es so: map (foldl (+) 0) (groupBy ... []) – mtzero

+0

Danke für die Erklärung, jetzt Ich verstehe es klarer. – mtzero

5

Sie können Ihr Problem in zwei Aufgaben

  1. Split eine Liste in mehrere Teile auf Nullen unterteilen.
  2. Summe Teile.

Für die erste Aufgabe haben wir Data.List.Split Modul, das splitOn Funktion exportiert. Es tut genau das, was wir brauchen:

> splitOn [1] [0,0,0,1,0,0,0,1,0] 
[[0,0,0],[0,0,0],[0]] 

Für die zweite Aufgabe es wohl bekannt ist map -function, die eine Funktion zum jeweiligen Elemente der Liste gilt. In unserem Fall ist diese Funktion sum:

> map sum [[1,2,3],[4,5,6],[7,8,9]] 
[6,15,24] 

So:

> :m +Data.List.Split 
> map sum . splitOn [0] $ [1,2,3,0,3,4,0,2,1] 
[6,7,3] 
+0

Dank für die schnelle Antwort. Gibt es eine Art andere anfängerfreundliche Lösungen? Oder was könnte ich mit dem Code machen, den ich bisher erreicht habe? – mtzero

+0

@mtzero Wenn es kein echtes Problem ist, sollten Sie Ihre Frage als Hausaufgabe markieren. –

1

Auch wenn Sie das split Paket zu installieren sind nicht in der Lage zu installieren und verwenden Data.List.Split als Matvey, Sie schlägt vor, kann immer noch den allgemeinen Ansatz verwenden:

  1. Split die seltsame Liste mit 0 Separatoren in eine konventionellere Liste von Listen.
  2. Summe jeder Liste.

So

yourFunction = map sum . split 

Jetzt müssen wir split schreiben.

split :: [Int] -> [[Int]] 

Im Allgemeinen, wenn wir auseinander, eine Liste ziehen wollen, etwas Neues zu synthetisieren, müssen wir eine Falte verwenden.

split = foldr cons nil where 

nil hier sollte alles, was Sie split [] sein wollen sein.

nil = --TODO: exercise for you; clue: NOT [] 

cons verbindet mittlerweile eine Ihre Zahlen, mit der Antwort aus dem vorherigen Schritt der Falte. Natürlich müssen Sie verschiedene Dinge tun, je nachdem, ob die Nummer 0 ist oder nicht.

cons 0 xss  = --TODO 
    cons x (xs : xss) = --TODO; why will this pattern match never fail? 
Verwandte Themen