Seite 1 von 1

Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 08:48
von Hyperion
Hallo,

ursprünglich ging es um eine Hausaufgabe der 5. Klasse, für die folgendes gilt:

Code: Alles auswählen

x % 2 = 1
x % 3 = 2
x % 4 = 3
x % 5 = 4
x % 6 = 6
x % 7 = 0

mit 1 <= x < 1000
Logisch, statt mit Denken löst man das schnell in Python per Brute-Force ;-)

Nun war mein Ansatz folgender:

Code: Alles auswählen

funcs = [lambda x: x % n == n-1 for n in range(2, 7)]

funcs.append(lambda x: x % 7 == 0)

for i in range(1, 1001):
    if all(f(i) for f in funcs):
        print(i)

> 35 77 119 161 203 245 287 329 371 413 455 497 539 581 623 665 707 749 791 833 875 917 959
Klappt leider nicht! :-( (die gesuchte Lösung ist [119, 539, 959])

Wenn ich die Lambdas "zu Fuß" generiere, funktioniert die Berechnung.

Offenbar ist das innere ``n`` bei den in der List-Comprehension generierten Lambda-Funktionen bei allen gleich der ``6``. Dies kann man mittels Ausgabe aller Lambda-Ergebnisse ohne das manuelle letzte leicht herausfinden.

Jetzt meine Frage: Wieso?

Ein kurzes googlen ergab, dass ich nicht der erste bin, der das sonderbar findet. Leider ist mir aktuell vollkommen unklar, wieso das nicht funktioniert!

Edit: Ich habe dafür ein Python 3.3.3 benutzt - falls das eine Rolle spiele sollte.

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 08:55
von Boa
Eine Zeile scheint falsch zu sein:
x % 6 = 5
Und probiere es vielleicht Mal mit Klammern um die Modulo Ausdrücke; Es kann sein, dass die ziemlich spät kommen in der Operator rang liste.

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 09:00
von Hyperion
Boa hat geschrieben:Eine Zeile scheint falsch zu sein:
x % 6 = 5
Ooops, stimmt :-)
Boa hat geschrieben: Und probiere es vielleicht Mal mit Klammern um die Modulo Ausdrücke; Es kann sein, dass die ziemlich spät kommen in der Operator rang liste.
Wo genau meinst Du denn, sollte man Klammern setzen?

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 09:02
von BlackJack
@Hyperion: Ist doch ein Klassiker. Das `n` wird beim *Aufruf* der ``lambda``-Funktion aufgelöst, und nicht beim *Erstellen*. Und wenn die Funktion aufgerufen wird, hat `n` den Wert des letzten Schleifendurchlaufs. Die LC und das ``lambda`` sind ja letztendlich nur Kurzschreibweisen für das hier:

Code: Alles auswählen

def create_funcs():
    result = list()
    for n in range(2, 7):
        def f(x):
            return x % n == n - 1
        result.append(f)
    return result
Wäre Dir hier klar warum `n` bei Aufrufen von `f` immer zu 6 aufgelöst wird?

Lösungen: Default-Werte, weil die ausgewertet werden wenn die Funktion *erstellt* wird und nicht wenn sie *aufgerufen* wird, oder `functools.partial()`:

Code: Alles auswählen

funcs = [lambda x, n=n: x % n == n - 1 for n in range(2, 7)]
# oder
funcs = [partial((lambda n, x: x % n == n - 1), n) for n in range(2, 7)]

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 09:11
von Hyperion
BlackJack hat geschrieben: Wäre Dir hier klar warum `n` bei Aufrufen von `f` immer zu 6 aufgelöst wird?
Ja, danke. Damit ist der Groschen gefallen! :-)

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 20:53
von Hyperion
Hier mal eine Lösung in Clojure - sieht fast aus wie die Lösung in Python :-)

Code: Alles auswählen

(defn make-func [x] 
  (partial #(= (mod %2 %1) (- %1 1)) x))

(defn make-funcs []
  (cons 
    #(zero? (mod % 7)) 
    (map make-func (range 2 7))))

(filter (apply every-pred (make-funcs)) (range 1 1001))

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 22:34
von BlackJack
Wenn ich so eine Hausaufgabe mit dem Rechner hätte lösen wollen, würde das wohl so aussehen:

Code: Alles auswählen

   10 DIM A(5),B(5):FOR I=2 TO 7:A(I-2)=I:B(I-2)=I-1:NEXT:B(5)=0
   20 FOR X=1 TO 999:FOR I=0 TO 5:C=X-INT(X/A(I))*A(I)=B(I):IF NOT C THEN I=5
   30 NEXT:IF C THEN PRINT X,
   40 NEXT
:-)

Re: Merkwürdiges lambda...

Verfasst: Montag 2. Juni 2014, 22:41
von Hyperion
Du meinst natürlich mit DEM Rechner schlechthin 8)

Re: Merkwürdiges lambda...

Verfasst: Dienstag 3. Juni 2014, 01:52
von snafu
Java 8 mit der Stream-API:

Code: Alles auswählen

import java.util.stream.IntStream;

public class FunctionalExperiments {
    public static void main(String[] args) {
        IntStream.rangeClosed(1, 1000).filter(x -> 
            IntStream.rangeClosed(2, 6).allMatch(n -> 
                x % n == n - 1
            )
            && x % 7 == 0
        ).forEach(System.out::println);
    }
}

Re: Merkwürdiges lambda...

Verfasst: Dienstag 3. Juni 2014, 12:24
von EyDu
Warum selber nachdenken:

Code: Alles auswählen

rule(X) :-
    between(1, 999, X),
    X mod 2 =:= 1,
    X mod 3 =:= 2,
    X mod 4 =:= 3,
    X mod 5 =:= 4,
    X mod 6 =:= 5,
    X mod 7 =:= 0.

Re: Merkwürdiges lambda...

Verfasst: Dienstag 3. Juni 2014, 14:44
von Boa
@EyDu: Ist das Prolog? Sieht zumindest deklarativ aus.

Re: Merkwürdiges lambda...

Verfasst: Dienstag 3. Juni 2014, 15:09
von Hyperion
Boa hat geschrieben:@EyDu: Ist das Prolog? Sieht zumindest deklarativ aus.
Japp, ist es. Hätte ich ja eigentlich von pillmuncher erwartet ;-)

(Wenn Du auf "Zitieren" eines Beitrags gehst, siehst Du übrigens auch die Angabe in der Syntax-Deklaration ;-) - ok, die könnte natürlich auch lügen :mrgreen: )

Re: Merkwürdiges lambda...

Verfasst: Dienstag 3. Juni 2014, 21:25
von jerch
Die Aufgabe lässt sich auch schön übers kgV vereinfachen zu:

Code: Alles auswählen

f = lambda x: (60*x + 17) * 7