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

putchar (B sprache)

Beitragvon stuhlbein » Donnerstag 7. Mai 2009, 01:58

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

Beitragvon martin101986 » Donnerstag 7. Mai 2009, 06:41

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

Beitragvon BlackJack » Donnerstag 7. Mai 2009, 07:00

@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

Beitragvon stuhlbein » Donnerstag 7. Mai 2009, 07: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

Beitragvon BlackJack » Donnerstag 7. Mai 2009, 07:24

@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

Beitragvon stuhlbein » Donnerstag 7. Mai 2009, 07:33

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

Beitragvon birkenfeld » Donnerstag 7. Mai 2009, 08:29

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

Beitragvon birkenfeld » Donnerstag 7. Mai 2009, 08:34

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

Beitragvon stuhlbein » Donnerstag 7. Mai 2009, 09:01

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

Beitragvon lunar » Donnerstag 7. Mai 2009, 09:03

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

Beitragvon birkenfeld » Donnerstag 7. Mai 2009, 09:20

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

Beitragvon sma » Donnerstag 7. Mai 2009, 09:51

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

Beitragvon birkenfeld » Donnerstag 7. Mai 2009, 09:58

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

Beitragvon sma » Donnerstag 7. Mai 2009, 10:11

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

Beitragvon stuhlbein » Donnerstag 7. Mai 2009, 10:40

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*

Wer ist online?

Mitglieder in diesem Forum: Google [Bot]