putchar (B sprache)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

Hallo :)

Ich versuche grad die "putchar" funktion aus der B Sprache nachzubauen.

Normalerweise werden Ascii escapes in B durch ein vorangehendes '*' deklariert.

Meine frage ist nun, wie man das am elegantesten in Python umsetzt.

Das Original aus B würde so aussehen:

Code: Alles auswählen

 main( ) {
 	putchar('hell');
 	putchar('o, w');
 	putchar('orld');
 	putchar('*n' );
 }
Die Ausgabe wäre "hello, world\012".

Mein bisheriges Ergebnis:

Code: Alles auswählen

import sys

def putchar(chars, out=sys.stdout, enc='utf-8'):
    special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}
    nchars = ''
    for char in chars: # kann natuerlich niemals funktionieren
        spc = special_chars.get(str(char))
        if spc:
            char.replace(char, spc)
        nchars += char
    out.write(unicode(nchars, enc))

# beispiel aufruf:

putchar('hell')
putchar('o, w')
putchar('orld')
putchar('*n' ) # das '*n' sollte eigentlich durch \012 ersetzt werden
Natürlich kann das in der form nie funktionieren, da 'for char in chars: [...]' ja jedes zeichen *einzeln* durchläuft.
Wie splitte ich den string nun also in zweier gruppen? Oder gibt es bessere methoden das zu lösen?


PS: falls es jemand interessiert warum ich mir das antue: just 4 fun! (kein scherz, ich mach das wirklich nur zum spass. Und um nebenbei ein wenig Python zu lernen. :))
Benutzeravatar
martin101986
User
Beiträge: 85
Registriert: Montag 3. Dezember 2007, 19:15
Wohnort: Steiermark, Österreich

Hallo,

du könntest schauen ob ein Sonderzeichen in deinem String ist und dieses dann ersetzen.

Code: Alles auswählen

text = 'Test*n'

special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}

for key in special_chars.keys():
    if key in text:
        text = text.replace(key, special_chars[key])

Grüße Martin
BlackJack

@Bitfish: Wieso an Zweigergruppen? Oder müssen die Sternchen immer an geraden Indexen beginnen?

Ich würde das mit einem regulären Ausdruck machen. Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?

Code: Alles auswählen

In [14]: special_chars
Out[14]:
{'**': '*',
 '*0': '\x00',
 '*a': '\x07',
 '*b': '\x08',
 '*f': '\x0c',
 '*n': '\n',
 '*r': '\r',
 '*t': '\t'}

In [15]: s = 'Hallo*n' + 'Keine Ersetzung: *x*n' + 'Einfacher Asterisk: **'

In [16]: print re.sub(r'\*[*ntrbaf0]', lambda m: special_chars[m.group(0)], s)
Hallo
Keine Ersetzung: *x
Einfacher Asterisk: *
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?

@martin101986: Ziemlich ineffizient.
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

martin101986 hat geschrieben:Hallo,

du könntest schauen ob ein Sonderzeichen in deinem String ist und dieses dann ersetzen.
Klappt tatsächlich... Da war ich mit meiner lösung ziemlich auf dem Holzweg ;)

Wär aber dennoch interessant zu erfahren, wie man Strings in zweier (oder dreier, vierer, etc..) gruppen aufsplittet, sofern sowas überhaupt geht ;)
BlackJack

@Bitfish: Wenn es '**' -> '*' wirklich gibt, geht das mit dem suchen und ersetzen nicht, wenn man nicht auf die Reihenfolge achtet. '**n' kann dann nämlich über '*n' zu '\n' werden, wenn man die '**' nicht erst ganz zum Schluss ersetzt.

Ansonsten ist das wie gesagt ziemlich ineffizient, weil die Zeichenkette für jeden `special_char`-Eintrag komplett durchlaufen wird. Wenn man überflüssigerweise vorher prüft ob die Zeichen überhaupt enthalten sind, wird's noch ineffizienter.

Ein Ansatz da Paarweise durchzugehen:

Code: Alles auswählen

In [18]: s = 'hallo'

In [19]: zip(s, s[1:])
Out[19]: [('h', 'a'), ('a', 'l'), ('l', 'l'), ('l', 'o')]
Oder man geht einzeln durch und merkt sich immer das letzte Zeichen.

Aufpassen sollte man auf jeden Fall bei den Sonderfällen, also zum Beispiel die leere Zeichenkette, oder eine in der nur ein einzelnes '*' steht.
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

BlackJack hat geschrieben:@Bitfish: Wieso an Zweigergruppen? Oder müssen die Sternchen immer an geraden Indexen beginnen?
Naja, Ich dachte es wäre einfacher, den string in zweiergruppen zu zerlegen, und in einer for-schleife gegen das special_chars dict zu prüfen.
Ich würde das mit einem regulären Ausdruck machen.
Warum?

Mit der von User:martin101986 genannten lösung kann ich vollkommen problemlos folgenden Code korrekt Interpretieren:

>>> putchar("*tFoo*0bar!*r*n*r*t*tNaechste zeile! *nZwei Sterne: ***n!")
Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?
Doch, in B's putchar() konnte man auch ganz normal ein einzelnes '*' wiedergeben, wenn darauf kein "interpretiertes" zeichen kam.
Das kann man relativ gut mit dem heute gebräuchlichen Backslash vergleichen:

"Foo\bar!" => das "\b" wird als backspace interpretiert
"Foo\\bar" => das "" und "\b" werden ausgegeben, der backspace wird nicht interpretiert

.. Demnach brauch man auch kein '**' in die tabelle einfügen, da das "* + zeichen" nur dann interpretiert wird, wenn auf das '*' auch ein zugeordnetes zeichen folgt! (ich hoffe es war klar worauf ich hinaus wollte ... :D)

Code: Alles auswählen

In [14]: special_chars
Out[14]:
{'**': '*',
 '*0': '\x00',
 '*a': '\x07',
 '*b': '\x08',
 '*f': '\x0c',
 '*n': '\n',
 '*r': '\r',
 '*t': '\t'}

In [15]: s = 'Hallo*n' + 'Keine Ersetzung: *x*n' + 'Einfacher Asterisk: **'

In [16]: print re.sub(r'\*[*ntrbaf0]', lambda m: special_chars[m.group(0)], s)
Hallo
Keine Ersetzung: *x
Einfacher Asterisk: *
Das ginge natürlich auch, das problem hierbei ist, wie bei allen arbeiten mit regulären ausdrücken, dass diese bei grossen dateien sehr lange dauern können (auch wenn das für die gezeigten beispiele irrelevant ist.)

Zugegebenermassen sieht deine lösung sauberer aus. Aber ich denke nicht, dass sie auch zwingend besser sein muss ;)
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?
Kann ich dir leider nicht beantworten, da B eine sehr alte programmiersprache ist, und es kaum verfügbare quelltexte gibt.
Ich vermute, dass `putchar()` diese zeichen ersetzt, da B nur sehr wenige eingebaute funktionen besitzt.
Aber das wäre echt interessant zu erfahren ... ;)
@martin101986: Ziemlich ineffizient.
Kann ich nicht zustimmen. Folgender code funktioniert hier prima:

Code: Alles auswählen

import sys

def putchar(chars, out=sys.stdout, enc='utf-8'):
    special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}
    nchars = ''
    for key in special_chars.keys():
        if key in chars:
            chars = chars.replace(key, special_chars[key])
    out.write(unicode(chars, enc))

putchar("*tFoo*0bar!*r*n*r*t*tNaechste zeile! *nZwei Sterne: ***n!")
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Bitfish hat geschrieben: Das ginge natürlich auch, das problem hierbei ist, wie bei allen arbeiten mit regulären ausdrücken, dass diese bei grossen dateien sehr lange dauern können (auch wenn das für die gezeigten beispiele irrelevant ist.)
Soso, reguläre Ausdrücke "dauern lange bei großen Dateien". Wo hast du denn das so allgemein gelesen? Und du meinst, die Lösung mit str.replace() ist besser? Mehr dazu siehe unten.
Zugegebenermassen sieht deine lösung sauberer aus. Aber ich denke nicht, dass sie auch zwingend besser sein muss ;)
Ist sie aber. Und bei BlackJack kann man sich da eigentlich sicher sein.
@martin101986: Ziemlich ineffizient.
Kann ich nicht zustimmen. Folgender code funktioniert hier prima:
Seit wann heißt "ineffizient" "funktioniert nicht"?

Martins Code ist deshalb ineffizient, weil der String für jedes Escape einmal durchlaufen wird, um ein Vorkommen zu finden (das ist ok), aber dann *noch ein zweites mal*, um dieses auch zu ersetzen. Sprich, das "find" vor dem "replace" ist unnütz.

Dann bleibt noch die Tatsache, dass der String für jedes Escape durchlaufen wird. Genau das spart man sich mit der Regex.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Bitfish hat geschrieben:
Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?
Doch, in B's putchar() konnte man auch ganz normal ein einzelnes '*' wiedergeben, wenn darauf kein "interpretiertes" zeichen kam.
Das kann man relativ gut mit dem heute gebräuchlichen Backslash vergleichen:

"Foo\bar!" => das "\b" wird als backspace interpretiert
"Foo\\bar" => das "" und "\b" werden ausgegeben, der backspace wird nicht interpretiert
Nein, gerade dieses Verhalten ist eben nicht vergleichbar mit dem Verhalten der Sternchen, du hättest als Beispiel z.B. "Foo\kbar" anbringen müssen.
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?
Kann ich dir leider nicht beantworten, da B eine sehr alte programmiersprache ist, und es kaum verfügbare quelltexte gibt.
Ich vermute, dass `putchar()` diese zeichen ersetzt, da B nur sehr wenige eingebaute funktionen besitzt.
Aber das wäre echt interessant zu erfahren ... ;)
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

birkenfeld hat geschrieben:
Martins Code ist deshalb ineffizient, weil der String für jedes Escape einmal durchlaufen wird, um ein Vorkommen zu finden (das ist ok), aber dann *noch ein zweites mal*, um dieses auch zu ersetzen. Sprich, das "find" vor dem "replace" ist unnütz.

Dann bleibt noch die Tatsache, dass der String für jedes Escape durchlaufen wird. Genau das spart man sich mit der Regex.
Verstehe :) In dem fall ist ein regulärer ausdruck natürlich besser. :D
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).
http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:

PS: Ich hab die funktion num um Blackjack's lösung umgeändert, da mir diese auch logischer erscheint. Danke!
lunar

Bitfish hat geschrieben:
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).
http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:
Ja und? Das widerspricht birkenfelds Aussage nicht.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:
Naja, das "when printed" bezieht sich wohl auf das physikalische Terminal.
Im "Appendix B" findet sich jedenfalls Klarheit:
"""
Escape sequences are used in character constants and strings to obtain characters which for one reason or another are hard to represent directly.
"""
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich würde das ja so machen:

Code: Alles auswählen

putchar_esc = False
putchar_specials = {'n': '\n'}

def putchar(s, out=sys.stdout):
    for c in s:
        global putchar_esc
        if putchar_esc:
            out.write(putchar_specials.get(c, c))
            putchar_esc = False
        elif c == '*':
            putchar_esc = True
        else:
            out.write(c)
Damit erwische ich auch den Fall `putchar("*"); putchar("n")` falls das ebenfalls einen Zeilenumbruch ergeben soll. Falls das unnötig ist, kann man `putchar_esc` zu einer lokalen Variable machen.

Stefan
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Das hat zwei Probleme: Erstens, '*n' ist wirklich ein Escape, also funktioniert es nicht in zwei Aufrufen, und zweitens, '*k' (unbekanntes Escape) soll wieder '*k' ergeben.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Hätte vielleicht vorher in die Sprachspezifikation schauen sollen. Ich fand nur komisch, dass niemand den IMHO einfachsten Weg vorschlug und stattdessen jeder mit Suchen-und-Ersetzen hantierte. Aus der ein bisschen zu knappen Sprachspezifikation rate ich, dass "*n" bereits vom Scanner in ein LF umgewandelt wird und nie in putchar ankommt. Ob ein *Q zu *Q oder Q wird (was ja bei unbekannten \-Escapes in C passieren würde) kann ich der Sprachspezifikation nicht entnehmen.

Entscheidend ist aber - egal ob's nun im Scanner oder der putchar-Funktion passiert - das eine primitive "state machine" einfacher ist als den kompletten Text jeweils mit verschiedenenen replace-Aufrufen zu durchsuchen.

Code: Alles auswählen

def putchar(s, out=sys.stdout):
    esc = False
    for c in s:
        if esc:
            if c in specials: out.write(specials[c])
            else: out.write("*"); out.write(c)
            esc = False
        elif c == '*': esc = True
        else: out.write(c)
Stefan
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

sma hat geschrieben: Aus der ein bisschen zu knappen Sprachspezifikation rate ich, dass "*n" bereits vom Scanner in ein LF umgewandelt wird und nie in putchar ankommt. Ob ein *Q zu *Q oder Q wird (was ja bei unbekannten \-Escapes in C passieren würde) kann ich der Sprachspezifikation nicht entnehmen.
Vermutlich. Ohne quelltext eines B compilers könnte man das eh nur erraten.
Entscheidend ist aber - egal ob's nun im Scanner oder der putchar-Funktion passiert - das eine primitive "state machine" einfacher ist als den kompletten Text jeweils mit verschiedenenen replace-Aufrufen zu durchsuchen.

Code: Alles auswählen

def putchar(s, out=sys.stdout):
    esc = False
    for c in s:
        if esc:
            if c in specials: out.write(specials[c])
            else: out.write("*"); out.write(c)
            esc = False
        elif c == '*': esc = True
        else: out.write(c)
Stefan
Ich versteh nicht, inwiefern deine Funktion irgendwie besser sein soll - Bei mir funktioniert sie nichtmal... :K

*edit*: Ich hab einen quellofenen BCPL Compiler finden können. Allerdings ist BCPL der vorgänger von B, und auch entsprechend ausgerüstet ... ;) Für interessierte aber dennoch sicher einen blick wert: http://www.cl.cam.ac.uk/~mr10/BCPL.html

ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein *augenroll*
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Bitfish hat geschrieben:ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein *augenroll*
Das verlangt ja auch keiner... Wenn du Fragen hast, werden sie sicher nicht unfreundlich beantwortet. Wenn du allerdings die Aussagen der Experten nicht glaubst, musst du dich darauf einstellen dass sie sie verteidigen...

Übrigens, viele Posts klingen nur deshalb "freundlich", weil jeder Satz mit einem Smiley abgeschlossen wird. Das halte ich aber auch nicht für eine sinnvolle Angewohnheit.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

birkenfeld hat geschrieben:Übrigens, viele Posts klingen nur deshalb "freundlich", weil jeder Satz mit einem Smiley abgeschlossen wird. Das halte ich aber auch nicht für eine sinnvolle Angewohnheit.
Ich finde die Smileyanhängerei bei unfreundlichen Posts um sie 'freundlich' zu machen auch für Bedenklich, jedoch ist es tatsache das gerade im Dialog und als solcher ist so eine Forendiskussion am ehesten einzuschätzen, einige weitere Informationen wie Ton, Mimik und Gestik übertragen werden, so dass man besser weiß wie die Aussage einzuschätzen ist. Das hat man in der reinen Textform eher nicht, daher benutzt man solche Krücken.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Bitfish hat geschrieben:ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein
... und manchmal wird man auch Jahre nach der Geburt und überhaupt zu Lebzeiten überhaupt keiner.

Eine nicht zu unterschätzende Eigenschaft ist aber sicherlich, wenn man seine eigenen Programmierfähigkeiten und Kenntnisse einzuschätzen und einzuordnen weiß; weiß, was man kann und was man nicht kann und sich dann von denen, die das können, was man (noch) nicht (oder vielleicht auch nie) kann, sagen zu lassen, wie es richtig oder besser geht.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Ihr seid alle doof und könnt nichts! :)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

audax hat geschrieben:Ihr seid alle doof und könnt nichts! :)
Ja, genauso hatte ich das gemeint. Eine gesunde Selbsteinschätzung gepaart mit einer annähernd objektiven Einschätzung der anderen ist Gold wert!
Antworten