Wahrheitswert in Scheme; Zusammenspiel zwischen Scheme und LilyPond

Begonnen von ingmar, Montag, 15. Juli 2024, 15:22

« vorheriges - nächstes »

ingmar

Hallo,

wahrscheintlich ein ganz blöder Fehler – folgender Code kompiliert anstandslos, hat aber nicht das erwartete Ergebnis:
\version "2.20.0"

AAA = "Hallo."
\markup "Test"

#(if ( string=? AAA "Hallo." )
     #{ \markup "JA!" #}
     #{ \markup "NEIN." #} )

Irgendwas muss an dem Vergleich faul sein, aber ich komm einfach nicht dahinter: Es wird weder "JA!" noch "NEIN." ausgegeben.

Sicher fällt euch der Fehler direkt auf...

Gruß,
--ingmar

(EDIT: Klarer formuliert)

Malte

Vor das #(if noch ein \markup setzen funktioniert. (Dafür sind die \markups innerhalb dann optional.)

ingmar

ah, danke! : - )

Es entsteht allerdings die Frage, warum das so ist. Offenbar ist die Übergabe von Guile zu LilyPond nicht ganz so simpel, wie man sich das von außen vorstellt.

Mit der Erzeugung von Strings als Variablen in Scheme und anschließenden Verwendung und Ausgabe der Variablen in LilyPond komme ich klar. Bei einem Boolean krieg ich es nicht hin. Hier ein anderer Versuch, der allerdings nicht kompiliert:

\version "2.20.0"

AAA = "Hallo."
 #( define is-aaa-hallo ( string=? AAA "Hallo." ))
\markup \is-aaa-hallo

LilypPond kommentiert: "Fehler: Keine Textbeschriftung" (was auch immer das heißen mag).

Gibt es irgendwo einen Artikel zu diesem Thema, wo man sich etwas aufschlauen kann? : - )

Danke, Gruß,
--ingmar

Manuela

#3
Was genau willst du erreichen? Soll getestet werden, ob AAA ein String ist?

\version "2.20.0"

%AAA = "Hallo."
AAA = #'(1 . 1)

#(define is-aaa-hallo (if (string? AAA) AAA "AAA ist kein String" ))

\markup \is-aaa-hallo

gibt den Inhalt von AAA aus, wenn AAA ein string ist, ansonsten den Alternativstring (in diesem Fall "AAA ist kein String")

string=? liefert einen Wahrheitswert, der natürlich kein Markup ist

Scheme dokumentation

Wenn du das Ergebnis des Tests als Markup ausgeben willst, musst du es in einen String umwandeln.

\version "2.25.6"

AAA = #'(1 . 1)

#(define (->string x)
  (call-with-output-string
   (lambda (out)
     (display x out))))

#(define is-aaa-hallo (->string (string=? (->string AAA) "Hallo." )))

\markup \line { AAA ist Hallo.: \is-aaa-hallo }
 
\markup #(->string AAA)

wobei zu beachten ist, dass string=? Strings als Argumente erwartet und einen Fehler meldet, wenn dies nicht der Fall ist.
Danke für eure Hilfe
viele Grüße
-- Manuela

ingmar

Zitat von: Manuela am Montag, 15. Juli 2024, 21:40Was genau willst du erreichen? Soll getestet werden, ob AAA ein String ist?

Ich probiere halt mit Scheme rum. Hier will ich einfach wissen, ob der String AAA einen bestimmten Wert hat, eben "Hallo."

Das \markup-Getöse war nur dazu da, auf einfache Weise (aus meiner LilyPond-Umgebung) zu sehen, ob es geklappt hat.

Zitatstring=? liefert einen Wahrheitswert, der natürlich kein Markup ist
ja, logisch. Ich hatte halt ohne nachzudenken unterstellt, dass jeder Datentyp implizit in einen String umgewandelt wird, wenn nötig, hier also "#t" oder auch "##t". In vielen Programmiersprachen ist das so, hier eben nicht.

Die Formulierung "Fehler: Keine Textbeschriftung" ist aus dem sehr isolierten Kontext der \markup-Funktion irgendwie verständlich, im größeren Kontext innerhalb des allgemeinen Textschwalls ist sie IMHO problematisch; unmittelbar verstanden hätte ich aber beispielsweise "Fehler: \markup erwartet String, nicht Boolean". Wahrscheinlich macht eine Diskussion der Fehlermeldungen von LilyPond hier im Forum sehr wenig Sinn.

Danke für deine Hilfe!

--ingmar

harm6

Hallo ingmar :)

zunächst mal grundsätzliches.

Wenn Du in ein, von der Versions-Angabe abgesehen, leeres ly-file einen string schreibst, also:
\version "2.24.3" "whatever"gibts einen error. Denn dieser string könnte z.B. auch der Name einer Funktion sein, deren Definition aber fehlt.

Wenn Du einen scheme-string nimmst:
\version "2.24.3" #"whatever"gibts zwar keinen Fehler, es ist ja valide data, aber sonst nichts, Du hast LilyPond ja nicht gesagt was damit tun...

Du kannst jetzt (wie schon geschrieben worden ist) \markup davor setzen, dann ist LilyPond klar was zu tun ist. Es gibt auch die Möglichkeit $ zu verwenden.
Ganz genau kann ich Dir nicht auseinander setzen, was da passiert, auf jeden Fall werden manche Interpretationsmechanismen quasi kurz geschlossen, also:
\version "2.24.3" $"whatever"
In neueren Versionen gibt es auch die markup-commands \if bzw \unless, die einer Bedingung folgend ein gegebenes markup drucken oder nichts. Die Bedingung muss als procedure mit den Argumenten layout und props formuliert werden. Also:
\version "2.24.3"

AAA = "Hallo."
     
\markup \if #(lambda (layout props) (string=? AAA "Hallo." )) "xxx"
\markup \unless #(lambda (layout props) (string=? AAA "Ciao." )) "yyy"

@Manuela
statt ->string selbst zu definieren, warum nicht das builtin object->string verwenden?

Gruß,
  Harm


ingmar

#6
Zitat von: harm6 am Dienstag, 16. Juli 2024, 11:48In neueren Versionen gibt es auch die markup-commands \if bzw \unless, die einer Bedingung folgend ein gegebenes markup drucken oder nichts.
Whow! – Bloß schade, dass das nur innerhalb von \markup geht. Aber immerhin! Ich bin sicher, dass \if mir dann später an vielen Stellen elegant weiterhelfen wird...

Denn leider habe ich zur Zeit halt nur Version 2.20 zur Verfügung, ein Umzug ist erstmal nicht möglich, das ist für Apple-User offenbar zu kompliziert und zeitraubend (siehe hier). Das kommt später.

Danke jedenfalls für den Hinweis auf das Dollarzeichen! Dollar scheint ja immer noch die Währung zu sein, die man überall versteht, im Scheme- wie im Liliputland... : - ) Aber auch Dollars sind harte Währung, sie kennen nicht True und False, und hier ist $(object->string (string=? AAA "Hallo.")) ein guter Tip. Das klappt auch in 2.20 und hilft weiter.

Gruß, danke,
--ingmar

EDIT: Klarer formuliert.

Manuela

Zitat von: harm6 am Dienstag, 16. Juli 2024, 11:48@Manuela
statt ->string selbst zu definieren, warum nicht das builtin object->string verwenden?
Ganz einfach, ich habe diese Funktion nicht gefunden bei meiner Recherche, habe so etwas wie "tostring" gesucht
Danke für eure Hilfe
viele Grüße
-- Manuela

harm6

Hier noch einige Gedanken, funktionieren auch mit 2.20.
\version "2.20.0"

%%%%%%%%%%%%%%%%%%%%
%% MARKUP
%%%%%%%%%%%%%%%%%%%%

%% Markup-commands \if, \unless von upstream, sowie custom \if-else
#(define-markup-command (if layout props condition? argument)
  (procedure? markup?)
  (if (condition? layout props)
      (interpret-markup layout props argument)
      empty-stencil))

#(define-markup-command (unless layout props condition? argument)
  (procedure? markup?)
  (if (condition? layout props)
      empty-stencil
      (interpret-markup layout props argument)))

#(define-markup-command (if-else layout props condition? argument1 argument2)
  (procedure? markup? markup?)
  (if (condition? layout props)
      (interpret-markup layout props argument1)
      (interpret-markup layout props argument2)))


AAA = "Hallo."
\markup \if #(lambda (layout props) (string=? AAA "Hallo." )) "xxx"
\markup \unless #(lambda (layout props) (string=? AAA "Ciao." )) "yyy"

BBB = "Ciao."
\markup
  \if-else #(lambda (layout props) (string=? AAA "Hallo." )) "true" "false"

%% Funktioniert
$(if #t
  #{ \relative { b4 c d e } #}
  #{ \relative { b'4 a g f } #})

%% Funktioniert nicht, wie bereits diskutiert
#(if #f
  #{ \transpose c ces \relative { b4 c d e } #}
  #{ \transpose c ces \relative { b'4 a g f } #})

%% Hier funktioniert #(if ..) da wir uns *innerhalb* von { ... }, also innerhalb
%% sequentieller Musik bewegen. Dem parser ist hier klar was gemeint ist.
{
  #(if #f
    #{ \transpose c ces \relative { b4 c d e } #}
    #{ \transpose c ces \relative { b'4 a g f } #})
}

%%%%%%%%%%%%%%%%%%%%
%% MUSIK
%%%%%%%%%%%%%%%%%%%%

%% Musikfunktion zum selektieren
musIf =
#(define-music-function (condition m1 m2)(boolean? ly:music? ly:music?)
  (if condition m1 m2))

\musIf ##t
  \relative { b4 c d e }
  \transpose c cis \relative { b4 c d e }

\musIf ##f
  \relative { b4 c d e }
  \transpose c cis \relative { b4 c d e }

Gruß,
  Harm

Manuela

(string=? AAA "Hallo." )
liefert eine Fehlermeldung, wenn AAA nicht vom Typ "string" ist, daher würde ich diese Funktion eher nicht verwenden oder nur in dieser Form

$(object->string (string=? (object->string AAA) "Hallo."))
Danke für eure Hilfe
viele Grüße
-- Manuela

Manuela

Ich habe das Beispiel ein bisschen abgeändert, vll kannst du etwas davon brauchen

\version "2.25.6"

#(define of-type
   (lambda (x)
     (cond
      ((ly:music? x)            "ly:music")
      ((ly:duration? x)         "ly:duration")
      ((ly:book? x)             "ly:book")
      ((ly:context? x)          "ly:context")
      ((ly:context-def? x)      "ly:context-def")
      ((ly:context-mod? x)      "ly:context-mod")
      ((ly:dimension? x)        "ly:dimension")
      ((ly:dir? x)              "ly:dir")
      ((ly:dispatcher? x)       "ly:dispatcher")
      ((ly:duration? x)         "ly:duration")
      ((ly:event? x)            "ly:event")
      ((ly:font-metric? x)      "ly:font-metric")
      ((ly:grob? x)             "ly:grob")
      ((ly:grob-array? x)       "ly:grob-array")
      ((ly:input-location? x)   "ly:input-location")
      ((ly:item? x)             "ly:item")
      ((ly:iterator? x)         "ly:iterator")
      ((ly:lily-lexer? x)       "ly:lily-lexer")
      ((ly:lily-parser? x)      "ly:lily-parser")
      ((ly:listener? x)         "ly:listener")
      ((ly:moment? x)           "ly:moment")
      ((ly:music-function? x)   "ly:music-function")
      ((ly:music-list? x)       "ly:music-list")
      ((ly:music-output? x)     "ly:music-output")
      ((ly:otf-font? x)         "ly:otf-font")
      ((ly:output-def? x)       "ly:output-def")
      ((ly:page-marker? x)      "ly:page-marker")
      ((ly:pango-font? x)       "ly:pango-font")
      ((ly:paper-book? x)       "ly:paper-book")
      ((ly:paper-system? x)     "ly:paper-system")
      ((ly:pitch? x)            "ly:pitch")
      ((ly:prob? x)             "ly:prob")
      ((ly:score? x)            "ly:score")
      ((ly:skyline? x)          "ly:skyline")
      ((ly:skyline-pair? x)     "ly:skyline-pair")
      ((ly:source-file? x)      "ly:source-file")
      ((ly:spanner? x)          "ly:spanner")
      ((ly:spring? x)           "ly:spring")
      ((ly:stencil? x)          "ly:stencil")
      ((ly:stream-event? x)     "ly:stream-event")
      ((ly:translator? x)       "ly:translator")
      ((ly:translator-group? x) "ly:translator-group")
      ((integer? x)     "integer")
      ((rational? x)    "rational")
      ((real? x)        "real")
      ((complex? x)     "complex")
      ((number? x)      "number")
      ((null? x)        "null")
      ((list? x)        "list")
      ((pair? x)        "pair")
      ((string? x)      "string")
      ((vector? x)      "vector")
      ((boolean? x)     "boolean")
      ((char? x)        "char")
      ((symbol? x)      "symbol")
      ((procedure? x)   "procedure")
      ((eof-object? x)  "eof-object")
      ((input-port? x)  "input-port")
      ((output-port? x) "output-port")
      ((macro? x)       "macro")
      ((void? x)        "void")
      ((promise? x)     "promise")
      ((scheme? x)      "scheme")
      (#t "undef")
      )))

#(define-markup-command (vergleich layout props text-1)
   (scheme?)
   (let* ((is-string (string? text-1))
          (mytype (of-type text-1))
          (m (if is-string
                 text-1
                 (object->string text-1))))
     (if is-string
         (if (string=? m "Hallo.")
             (interpret-markup layout props
               #{
                 \markup
                 \line
                 { Der Text $m ist Hallo. }
               #})
             (interpret-markup layout props
               #{
                 \markup
                 \line
                 { Der Text $m ist nicht Hallo. }
               #})
             )
         (interpret-markup layout props
           #{
             \markup
             \line
             { Der Ausdruck $m ist kein String, sondern ein $mytype }
           #})
         )
     ))

\markup \vergleich "xxx"
\markup \vergleich "Hallo."
\markup \vergleich #'(1 . 1)
\markup \vergleich #'()
\markup \vergleich ##f
\markup \vergleich #'#(1 2 3)
\markup \vergleich #(ly:make-moment 1/6)
Danke für eure Hilfe
viele Grüße
-- Manuela

ingmar

Vielen Dank!

Es ist sehr nett, wie Ihr Euch um mich kümmert (und spekuliert, was ich brauchen könnte...)! : - )

Ich glaube, für den Moment komme ich klar. Ich versuche immer, meine Anfragen sehr allgemein zu stellen, damit sie jedem helfen können. Und oft stellt sich dabei heraus, dass das Problem an ganz anderer Stelle lag – oft auch einfach an meiner fehlenden Vertrautheit mit der Scheme-Syntax und den nicht nur hier diskutierbaren Fehlermeldungen.

Gruß,
--ingmar

ingmar

Hier ein ähnliches Problem.

Ich versuche, aus Scheme heraus zwei Dateien nachzuladen, wieder abhängig von einem "Wahrheitswert".

Mein Minimalbeispiel:
\version "2.20.0"

\markup "start"
% #(if #t  #{
      \include "abc.ly"
      \include "def.ly"
%    #})
\markup "ende"

Neben dem Code brauchts natürlich noch die beiden Dateien, die parallel zur Grunddatei (also im selben Folder) liegen müssen:

1. Datei:
Name: abc.ly
Inhalt: \version "2.20.0"  \markup "abc"

2. Datei:
Name: def.ly
Inhalt: \version "2.20.0"  \markup "def"

(Sinn des \markup ist natürlich, zu prüfen, ob das richtige File nachgeladen wurde.)

Lass ich das so laufen wie's dasteht, erhalte ich wie erwartet folgenden Output:
   start
   abc
   def
   ende


Frage 1:
Ich lasse nun den Scheme-Code wirken, indem ich in den beiden auskommentierten Zeilen das initiale "%" wegnehme. – Resultat:
   start
   ende


Die beiden eingebundenen Dateien werden nicht akzeptiert: "Fehler: Beschriftung außerhalb von Textbeschriftung oder \lyricmode". Mit ein bisschen Experimentieren wird klar, dass in dieser einzubindenden Datei nun plötzlich Noten erwartet werden! Frage: Warum das? Was genau hat der Scheme-Code da geändert?

Frage 2:
In der Version mit Scheme-Code kommentiere ich nun die vorletzte Zeile (\include "def.ly") aus. – Resultat ebenfalls:
   start
   ende


...aber hier gab es nichtmal eine Fehlermeldung! Als wäre alles in Ordnung. Frage natürlich: Wie kommt das? Könnte es sein, dass im LilyPond-Code (im Bereich #{ ... #} ) nur ein einziges Statement stehen darf? Oder was hab ich da sonst angerichtet?

Danke, Gruß,
--ingmar : - )


(Edit: einen Satz ergänzt und nochmal neu formuliert.)

harm6

Hallo ingmar :)

ich bin nicht 100-prozentig sachkundig was die Konstruktion #{ ... #} angeht und so kann meine Antwort unvollständig sein...

LilyPond versucht aus dem was innerhalb von #{ ... #} steht einen Sinn zu machen (was gar nicht so einfach ist) und einen Ausdruck zurückzugeben.
Es wird immer zuerst versucht einen musikalischen Ausdruck zu erstellen. Es läuft also auf
(make-music 'SequentialMusic 'elements (list ... ))
heraus.
Falls das nicht funktioniert und ein einzelnes \markup eingegeben wurde, so wird das auch ausgegeben.
Ob es noch andere Möglichkeiten gibt weis ich nicht.

Aber damit ist auch klar warum Dein Versuch scheitert.
Zwei \markup wären valider input in \lyricmode, der aber nicht angegeben ist. Oder als TextScript in Music. Wie die message ja sagt.
Es funktioniert also:
\new Lyrics $#{ \lyricmode { \markup "x" \markup "y" } #}und
{ b $#{ -\markup "x" -\markup "y" #} }
Aber das willst Du ja nicht haben...

Eventuell aber:
\markup "start"
$@(if #t
     (map
       (lambda (file) #{ \include #file #})
       (list
         "abc.ly"
         "def.ly")))
\markup "ende"

Jetzt gibt es nur jeweils ein markup in #{ #}.
Die enstandene Liste mir markups wird aufgespalten mittels @
Das $-Zeichen fügt die Resultate dann direkt ein, im Unterschied zum #-Zeichen.

Gruß,
  Harm

ingmar

#14
AHA!!! Das war dann auch der Grund, warum Malte mir ganz zu Beginn des Threads riet, das \markup vor mein if zu setzen. Mein Verdacht mit dem einzelnen erlaubten Statement war in dieser Form falsch, aber eigentlich gar nicht so blöd...

Mir geht es aber überhaupt nicht um das \markup. Ich hatte mich nur im Alltag daran gewöhnt, so auf – wie ich dachte – ganz einfache Weise zwischendrin "Hier bin ich!" sagen zu können.

Tatsächlich geht es mir darum, innerhalb eines Zweigs einer if-Konstruktion mehrere Files einzubinden. Diese Files enthalten aber eigentlich fast ausschließlich Definitionen und Zuweisungen – einige harmlose Variablen (pun intended ;-), und vor allem eine ganze Menge von Scheme-Funktionen und Music-Funktionen.

Vielleicht würde die Sache also funktionieren, wenn ich nun jede einzelne dieser Definitionen mit einem immergleichen if-Kränzchen umwickeln würde? So dass also nicht ein für alle mal entschieden wird, ob das File Berücksichtigung findet, sondern immer neu für jede einzelne Definition? Natürlich geht das (kommt mir aber nicht gerade elegant vor, und würde vermutlich jedes Ändern und Testen aufs Neue umständlich machen. Aber warum nicht...).

Jedenfalls, erstmal herzlichen Dank, das Problem wird mir jetzt viel klarer...

--ingmar