Frage Haskell - Mapping der ungerade gesetzten Werte und der gerade gesetzten Werte unterschiedlich


gibt es einen einfachen Weg. Um eine Liste von Zahlen zu nehmen, sagen wir 123456. Dann multipliziere die ungerade Zahl mit drei und die gerade Zahl mit 1.

d.h. (1 * 3) + (2 * 1) + (3 * 3) + (4 * 1) + (5 * 3) + (6 * 1)

Ich dachte, die Karte funktioniere irgendwo entlang der Linien. Aber ich weiß nicht, wie man * 3 nur auf die ungerade gesetzten Werte abbildet. Oh, und wenn Sie mir die Version nicht im Vorspiel geben könnten, wäre das großartig wie die eigentliche Funktion oder Funktionen, als ob sie aus einer externen Haskell-Datei importiert würde

Danke für die Hilfe


5
2018-05-31 07:45


Ursprung


Antworten:


Okay, wie ich zuvor in einem Kommentar geschrieben habe, zipWith (*) (cycle [3,1]) xs ist was du suchst. Aber zuerst ein kleiner Nitpick: den Kopf der Liste würde ich das nullte Element nennen, deshalb habe ich die 1 und 3 umgestellt :-)

Lassen Sie uns ein einfaches Beispiel durchgehen; Lassen xs Sein [9,8,7,3,2]. cycle [3,1] wiederholt einfach sein Argument immer wieder, so dass es eine unendliche Liste sein wird, die mit [3,1,3,1,3,1, ..] beginnt. Was zipWith f xs ys tut ist das Kopfelement von xs und das Kopfelement von ys und bewerben f (was eine Funktion von zwei Argumenten sein sollte) zu diesen Elementen - das Ergebnis von f geht dann auf die Vorderseite des Ergebnisses von zipWith. Wenn eines von xs oder ys leer wird, sind wir fertig; sonst machen wir einfach weiter.

Also wird das erste Element des Ergebnisses sein (3 * 9), dann (1 * 8), (3 * 7), (1 * 3), (3 * 2) und wir sind fertig!

Sie können sich die Definition von zipWith  Hier.

Wenn du Ja wirklich Wenn Sie die vordefinierten Funktionen nicht verwenden möchten, können Sie eine "alternierende Karte" definieren, die zwei Funktionen anstatt einer verwendet, indem Sie die erste auf den Kopf der Argumentliste anwenden und die Funktionen im rekursiven Aufruf umschalten. Ich werde dich die Details dort herausfinden lassen ...


14
2018-05-31 10:53



Was ist damit, angenommen? xs ist deine Liste von Zahlen:

map (uncurry ($)) (zip (cycle [((*) 1), ((*) 3)]) xs)

So funktioniert das:

[((*) 1), ((*) 3)] ist eine Liste mit zwei Funktionen. Der erste multipliziert eine Zahl mit 1; der zweite multipliziert eine Zahl mit drei.

cycle [...] erstellt eine unendliche Liste dieser beiden Funktionen und wiederholt nacheinander [× 1, × 3, × 1, × 3 ...]

zip (cycle [...]) xs nimmt Ihre Zahlen und verbindet sie mit den Funktionen. Also, wenn xs ist [1..6], dann erhalten Sie [(× 1, 1), (× 3, 2), (× 1, 3), (× 3, 4), (× 1, 5), (× 3, 6)].

Das $ :: (a -> b) -> a -> b Funktion ist ein Kombinator; es nimmt eine Funktion a → b und wendet sie auf einen Wert an. Damit map (uncurry ($)) (zip ...) nimmt die Liste der (Funktions-, Zahlen-) Paare und wendet die Funktion auf die Zahl an. Du brauchst uncurry Da die Liste Paare enthält, muss also die Signatur der zu mappenden Funktion sein ((a -> b), a) -> b.

Dies hinterlässt Sie mit der resultierenden Liste; in diesem Fall [1, 6, 3, 12, 5, 18].


2
2018-05-31 08:24



Mein erster Gedanke war, eine akkumulierende Karte zu verwenden und den akkumulierenden Parameter zu verwenden, um zu kennzeichnen, ob es sich um einen "geraden" oder "ungeraden" Zyklus handelt. Ich sehe, dass sonst niemand das getan hat, also lass uns sehen, wie es weitergeht ...

import Data.List (mapAccumL)

yourFunc = snd . mapAccumL mult True -- 'True' represents an odd index, starting from 1
  where
    mult True  x = (False, 3 * x)
    mult False x = (True , x)

1
2018-06-01 08:51



Wenn Sie diesen Punkt-freien Stil machen wollen, gibt es keinen besseren Weg als zu verwenden cycleund irgendeine Form von zip. (Punktfrei bedeutet grob "ohne Namensgebung für gebundene oder lambda-gebundene Variablen".) Ich habe zwei Fragen:

  • Wie viele verschiedene Möglichkeiten benötigen Sie, um diese Berechnung zu verallgemeinern?
  • Wie vertraut sind deine Leser? cycle und andere weniger bekannte Listenfunktionen (es muss hundert Listenfunktionen geben)?

Je allgemeiner dein Problem ist und je vertrauter deine Leser sind, desto eher empfehle ich dir einen punktfreien Stil. Für eine einmalige Lösung für Leser, die mit Haskell nicht vertraut sind, könnte ich eine rekursive Funktion versuchen:

mult31 [] = 0
mult31 [x:x':xs] = x * 3 + x' * 1 + mult31 xs
mult31 [x] = x * 3

Oder wenn Sie clever werden wollen, können Sie ein paar alternierende Funktionen verwenden:

mult31 = m3
  where m3 (x:xs) = x * 3 + m1 xs
        m3 []     = 0
        m1 (x:xs) = x * 1 + m3 xs
        m1 []     = 0

Eine dieser Funktionen erscheint einem Haskell-Veteranen weniger natürlich als etwas, das eine Zip-Funktion verwendet cycle, aber für jemanden, der gerade erst anfängt, sind sie vielleicht leichter zu folgen.


0
2018-05-31 14:21



Du kannst immer noch map verwenden, benutze zuerst zip:

let list' = zip [1..] list
in map (\(cnt,val) -> if odd cnt then val * 3 else val) list'

Oben sehen Sie, wie Sie ein Ergebnis abhängig vom Index und dem Wert in diesem Index machen können. Dies ist eine ziemlich allgemeine Lösung, die durch Ersetzen des Lambda (erstes Argument von map), können Sie viele Varianten machen. Die spezialisiertere Lösung mit cycle und zipWith ist viel kürzer und für diejenigen, die sich in Haskell wohlfühlen, perfekt lesbar.

bearbeitet und nach der Aussage ausgearbeitet nicht Hausaufgaben.


0
2018-05-31 08:11