Töne zählen: wie oft kommt ein Ton in einer Liste vor

Begonnen von Multimax, Freitag, 1. Juni 2018, 20:09

« vorheriges - nächstes »

Multimax

Hallo zusammen,

ich habe eine Funktion geschrieben, die einen Ton einer Melodie grün färben soll, wenn er in einer Referenzliste mehr als einmal vorkommt.

Die Funktion mehrals einmal schickt die Tonhöhe an eine Zählfunktion die Zählt wie oft die Tonhöhe (ly:music-property m 'pitch) in der Referenzliste vorkommt. Ist diese Anzahl größer als 1, soll der Ton grün gefärbt werden, ansonsten rot.

Irgendwas stimmt leider mit der Zählfunktion nicht. Ich weiß, dass sie funktioniert, denn: ersetze ich Zeile 21 durch

(> (zaehler 4 '(3 4 4)) 1)

werden die Töne grün. Irgendwas stimmt also mit dem Datentyp der Liste oder dem gesuchten Element nicht..

Habt Ihr vielleicht einen Hinweis, was ich falsch mache. Ich wäre sehr dankbar, sitze nämlich schon ziemlich lange daran.

\version "2.18.2"

\language "deutsch"

toene = {c' c' e' g' c''} %referenzliste
#(define toene (event-chord-pitches  #{ \toene #}))

#(define (zaehler x L) ;zählt, wie oft element x in Liste L vorkommt
(if (null? L)
0
(if (eq? x (car L))
(+ 1 (zaehler x (cdr L)))
(zaehler x (cdr L)))))

mehralseinmal=
#(define-music-function (parser location music) (ly:music?)
   (music-map
    (lambda (m)
      (if (and
           (music-is-of-type? m 'note-event)
             (> (zaehler (ly:music-property m 'pitch) toene) 1)
             )
           (begin
          (ly:music-set-property! m 'tweaks
            (acons 'color green
              (ly:music-property m 'tweaks)))
           m)
          (begin
          (ly:music-set-property! m 'tweaks
            (acons 'color red
              (ly:music-property m 'tweaks)))
           m)       
          ))
    music))

\new Staff  \relative {

\mehralseinmal {c' d e f g a h c d e f g} a a a a

}

harm6

ZitatIrgendwas stimmt leider mit der Zählfunktion nicht. Ich weiß, dass sie funktioniert, denn: ersetze ich Zeile 21 durch

(> (zaehler 4 '(3 4 4)) 1)

werden die Töne grün. Irgendwas stimmt also mit dem Datentyp der Liste oder dem gesuchten Element nicht..

In Deiner Zähl-procedure stimmt in der Tat etwas nicht: Die Gleichheitsprüfung ist für Dein Vorhaben ungeeignet.

In der scheme-sandbox (aufrufbar im Terminal mit 'lilypond scheme-sandbox'):
Zitat von: scheme-sandbox in terminal
guile> (eq? (ly:make-pitch 0 0) (ly:make-pitch 0 0))
#f
guile> (eqv? (ly:make-pitch 0 0) (ly:make-pitch 0 0))
#f
guile> (equal? (ly:make-pitch 0 0) (ly:make-pitch 0 0))
#t
guile>

Ersetze eq? durch equal? und es funktioniert.
Zu den Unterschieden von eq?, eqv? und equal? schau ins guile-manual. Es gibt auch noch andere Gleichheitsprüfungen wie string=? und andere.

Noch ein paar Bemerkungen zu 'zaehler'
Es ist nicht tail-rekursiv, das kann bei sehr langen Listen problematisch werden.

Du könntest stattdessen so vorgehen:

(define (zaehler-h x L)
  (define (helper object lst counter)
    (if (null? lst)
        counter
        (helper
          object
          (cdr lst)
          (if (equal? object (car lst))
              (1+ counter)
              counter))))
  (helper x L 0))


Zum Unterschied schau Dir an wie's arbeitet, wieder in der scheme-sandbox:
Zitat
guile> (define lst '(1 "a" 2 3 4 "a" 563 4 3 "a"))
guile> (define (zaehler x L) ;zählt, wie oft element x in Liste L vorkommt
   (if (null? L)
      0
      (if (equal? x (car L))
         (+ 1 (zaehler x (cdr L)))
         (zaehler x (cdr L)))))
guile> (use-modules (ice-9 debug))                           
guile> (trace zaehler)
(zaehler)
guile> (zaehler "a" lst)
[zaehler "a" (1 "a" 2 3 4 "a" 563 4 3 ...)]
[zaehler "a" ("a" 2 3 4 "a" 563 4 3 "a")]
|  [zaehler "a" (2 3 4 "a" 563 4 3 "a")]
|  [zaehler "a" (3 4 "a" 563 4 3 "a")]
|  [zaehler "a" (4 "a" 563 4 3 "a")]
|  [zaehler "a" ("a" 563 4 3 "a")]
|  |  [zaehler "a" (563 4 3 "a")]
|  |  [zaehler "a" (4 3 "a")]
|  |  [zaehler "a" (3 "a")]
|  |  [zaehler "a" ("a")]
|  |  |  [zaehler "a" ()]
|  |  |  0
|  |  1
|  2
3
3
guile> (define (zaehler-h x L)
  (define (helper object lst counter)
    (if (null? lst)
        counter
        (helper
          object
          (cdr lst)
          (if (equal? object (car lst))
              (1+ counter)
              counter))))
  (helper x L 0))
guile> (trace zaehler-h)
(zaehler-h)
guile> (zaehler-h "a" lst)
[zaehler-h "a" (1 "a" 2 3 4 "a" 563 4 3 ...)]
3
3
guile>

Aber warum überhaupt eine Rekursion?

Zumindest soweit Dein Beispiel reicht, ginge ja auch:
#(define (zaehler-h2 x L) (length (filter (lambda (e) (equal? e x)) L)))


Gruß,
  Harm

Multimax

Hallo Harm,

vielen Dank für deine sehr ausführliche Antwort. Bevor ich eine qualifizierte Rückantwort geben kann, muss ich erstmal ein paar Hausaufgaben machen.  :)
Bin zwar schon dreijähriger Lilyponduser aber noch Scheme-Anfänger. Deswegen muss ich für mich  erstmal noch zwei-drei Grundbaustellen klären, bevor ich Deine Antwort in Gänze verstehen kann. Das dauert bestimmt zwei-drei Tage, da ich am Wochenende erstmal anderweitig beschäftigt bin.

Viele Grüße

Multimax


Multimax

Hallo Harm,

Zitatn Deiner Zähl-procedure stimmt in der Tat etwas nicht: Die Gleichheitsprüfung ist für Dein Vorhaben ungeeignet.

Das hat man dann davon, wenn man einfach irgendeinen Code ohne Verständnis kopiert. Dieser Code stammt aus einer Übungsaufgabe.

Alles habe ich noch nicht verstanden, was die Rekursionsarten angeht. Aber die einzeilige Zählfunktion ist natürlich am elegantesten. Damit habe ich mit der Filter-Funktion schon wieder eine neue Funktion kennengelernt.