• Willkommen im Geoclub - dem größten deutschsprachigen Geocaching-Forum. Registriere dich kostenlos, um alle Inhalte zu sehen und neue Beiträge zu erstellen.

Lua-Funktionen mit variabler Anzahl Argumente

Wie man kürzlich schon mal in einer meiner Fragen lesen konnte, arbeite ich gerade daran eine meiner Cartridges mit Urwigo mehrsprachig zu machen (und bevor jemand fragt: Ich möchte bei Urwigo bleiben, mag ich einfach zu sehr).

Als Grundlage habe ich diesen Blogbeitrag von Krolock verwendet: Einen Wherigo in verschiedenen Sprachen anbieten.

Grundlage dafür ist eine LUA-Funktion, welche eine variable Anzahl Argumente entgegennimmt, das sieht so aus:
getMessageDynamic.PNG

Im Emulator hat das natürlich wie so oft auf prima geklappt und ich gehe mal davon aus, dass es auch auf dem Garmin funktioniert, denn sonst hätte Krolock das wohl nicht so veröffentlicht.

Auf Android wird die folgende Fehlermeldung ausgespuckt (mit anschliessendem Absturz der Catridge):
java.lang.RuntimeException: _len not defined for operand
.

Und auch auf dem iPhone (wohl 4 scheint mir aber hier nebensächlich) meines Bruders klappte es nicht:
FehleriPhone.jpg

Hier geht es einfach weiter, aber die Ersetzungen werden nicht ausgeführt. Ich vermute mal, die Funktion bricht ab, aber die Cartridge läuft an sich weiter:
es-geht-weiter.jpg

Nun die Frage: Sieht jemand an Krolocks Code Optimierungspotential, so dass man das zum Laufen kriegt?

Klar, ich könnte einfach die Funktion so schreiben, dass sie eine fixe Anzahl Argumente entgegennimmt, welche ich auf das Maximum der in dieser Cartridge benötigten Argumente setze und ggf. bei weniger Argumenten leere Strings übergeben. Aber wenn es irgendwie geht, würde ich das eigentlich ganz gerne vermeiden.

Hat jemand eine andere Idee?
 

jonny65

Geomaster
Das kööööönte doch das gleichen Phänomen sein, das Integer <> String Problem :
http://forum.geoclub.de/viewtopic.php?f=74&t=69256
 

Krolock

Geocacher
Ich hatte mich bzgl. varargs auf Charlenni verlassen, der schrieb er hab es bereits desöfteren eingesetzt
http://forum.geoclub.de/viewtopic.php?f=74&t=63276&start=20&hilit=vararg#p1057960
 

Charlenni

Geomaster
So, habe mir das jetzt mal angeschaut. Hier dürfte das Problem sein, dass OpenWIG bzw. iPhone Player Lua nicht richtig implementieren.

Bei OpenWIG ist das kein Problem. Da kannst Du einfach die folgende Zeile einfügen und es läuft:
Code:
local arg = {...}
Eigentlich unnötig, da Lua arg selbst definieren sollte, aber bei OpenWIG anscheinend nicht.

Bei dem iPhone Player wird es dann noch etwas rätselhafter. Die Lua Alert bezieht sich auf die Definition der Funktion. Na ja, etwas technisch: es geht um den Binary Chunk der Cartridge. Da dieser Wert angezeigt wird, nehme ich an, dass variable Parameter auf dem iPhone Probleme bereiten. Ich werde mal den Author kontaktieren und schauen, ob es eine Lösung gibt.

Es tut mir leid, wenn die von mir gemachten Vorschläge Probleme bereitet haben.
 

Charlenni

Geomaster
Habe mich gerade von Jan aufklären lassen. Die Variable "arg" für die variablen Argumente gibt es eigentlich nur in Lua 5.0 und in Lua 5.1 werden sie noch mitgeführt, sollen aber nicht mehr verwendet werden. Seine Lua-Implementierung hat sie komplett rausgeworfen :p . Deshalb ist der oben genannte Workaround auch die richtige Lösung. Diese funktioniert auch auf den Garmin Geräten und dem Emulator. Nur auf die Antwort vom iPhone Player warte ich noch.

@Krolock: Eventuell könntest Du das in Deinem interessanten Blog-Beitrag noch ändern. Übrigens wäre ich mit der for-Schleife vorsichtig. Der Parameter "..." hat außer den Werten auch noch den Eintrag "n", in dem die Anzahl der Werte steht. Deshalb habe ich seinerzeit auch ipairs vorgeschlagen
Code:
for i,v in ipairs{...} do
end
wäre dann das Richtige. So werden nämlich nur die durchnummerierten Werte abgefragt und nicht "n". Wenn Du weiter auf auf die for-Schleife nach Deinem Modell bestehst, dann besser
Code:
for i=1,arg.n do
end
verwenden.
 
OP
W

whiterussian_ch

Geocacher
So, nachdem nun der iPhone-Player aktualisiert ist, nehme ich das wieder auf.

Zwei Fragen habe ich noch, bin noch nicht extrem fit in der Syntax:
1. Das
Code:
local arg = {...}
muss zu Beginn der getMessage-Funktion stehen, richtig?

2. Wie müsste genau die "saubere" Version mit
Code:
for i,v in ipairs{...} do
aussehen? Bin etwas überfordert.

Mein Zwischenstand (inkl. 1. oben) sieht so aus:
Code:
function getMessage(key, ...)
  local arg = {...}
  local value = messages[language][key]
  if value == nil then
    value = messages[messages.defaultLanguage][key]
  end
  
  for i = 1, #arg do
    value = string.gsub(value, "#" .. tostring(i) .. "#", arg[i])
  end
  
  return value
end
 
OP
W

whiterussian_ch

Geocacher
Ach ja, Nachtrag: Mit obiger Version konnte ich das Android-Problem lösen, da funkioniert es jetzt, danke! Frage 1 scheint damit erledigt zu sein...
 
OP
W

whiterussian_ch

Geocacher
Und noch ein Nachtrag (sorry, für die "Zerstückelung"): Gerade erreicht mich eine Nachricht meines iPhone-Betatesters: Obiger Code funktioniert leider auf dem iPhone nicht...
 

Charlenni

Geomaster
Da wäre interessant, was genau nicht funktioniert: das tostring(i), die Verknüpfung mit .. oder das gsub. Eventuell mal antesten.
 
OP
W

whiterussian_ch

Geocacher
Also wenn ich das richtig verstehe mag der iPhone-Player einfach nicht, wenn er nicht genau weiss wie viele Argumente eine Funktion hat.
 

Charlenni

Geomaster
Das heißt, dass die ... nicht funktionieren. Schlecht. Nun ja, der iPhone Player hat keine vollständige Lua Implementierung. Damit funktionieren manche Dinge nicht. Du kannst Dich höchstens an Shawn (Programmierer des iPhone-Players) wenden, ob er eine Lösung weiß.
 

schnuefelis

Geonewbie
Tschuldigung, dass ich als neuling im Forum dieses alte Thema nochmals "aufwärme", aber ... :roll:
Ich baue gerade einen mehrsprachigen WIG und habe darum zum Thema vararg und string.gsub() in den letzten Tagen ein paar Versuche gemacht (mit freundlicher Unterstützung von Charlenni :gott:) und dabei folgendes herausgefunden, was ich euch nicht vorenthalten möchte:
- Der Wherigo-Player auf dem iPhone mag DEFINITIV KEINE vararg (...). :kopfwand:
- Die Funktion string.gsub() ist auf besagtem Player nicht richtig implementiert. Das Resultat sind immer leere Strings (nil). Sie muss mittels string.find() und string.sub() "nachgebaut" werden
Nimmt man nun die von whiterussian_ch zu Beginn gezeigte Funktionalität mit der Mehrsprachigkeit ( (c) by Krolock ;)) und dem ersetzen von Platzhaltern, so konnte ich die Sache folgendermassen auf iPhone, Android und Garmin erfolgriech zum Laufen bringen:

Code:
function getMessage (key, replace1, replace2, replace3, replace4)
  local value = messages[objLanguage][key]

-- Ersetzen der #n# Platzhater. Funktionbricht ab, sobald der erste "nil"-Wert gesehen wird
  if replace1 ~= nil then
    value = string.replace(value, "#1#", replace1)
    if replace2 ~= nil then
      value = string.replace(value, "#2#", replace2)
      if replace3 ~= nil then
        value = string.replace(value, "#3#", replace3)
        if replace4 ~= nil then
          value = string.replace(value, "#4#", replace4)
        end
      end
    end
  end
  return value
end

function string.replace(str, old, new)
  first,last = string.find(str, old) --Sucht in "str" die Zeichenkette "old" und gibt den Start und Endwert derselben zurück
  if first == nil then  -- Keine Üereinstimmung gefunden -> Abbruch
    return str
  end
  return string.sub(str, 1, first-1) .. new .. string.sub(str, last+1) -- Setzt an der gefundenen Stelle "new" ein
end

Je nach dem, wieviele #n# Platzhalter in der eigenen Karte vorkommen, kann die getMessage-Funktion und die darin enthaltene (zugegeben nicht sehr elegante...) if-Abfragefolge gekürzt oder erweitert werden.
Natrürlich ist in string.replace die Abfrage "if first == nil then" in diesem Fall nicht nötig, weil ich die Funktion ja schon gar nicht aufrufe wenn kein Platzhalter da ist. Der Code ist so aber universell einsetzbar .

Happy coding
 

Charlenni

Geomaster
Um das Ganze noch etwas universeller zu machen, anbei die Funktion für beliebig viele vorkommen des gesuchten Strings.
Code:
function string.replace(str, old, new)
  -- Looking in str for old, returns first and last
  first,last = string.find(str, old)
  -- If we found something ...
  while first ~= nil do
    -- ... than replace old string with new string one
    str = string.sub(str, 1, first-1) .. new .. string.sub(str, last+1)
    -- Look for the next occurence of old in str
    first,last = string.find(str, old)
  end
  return str
end
Damit sollte sich dann auf iPhones die Funktion string.gsub() in manchen Fällen ersetzen lassen.
 
Oben