Dualzahlenrechner

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.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Leonidas hat geschrieben:Es geht darum, dass es ja immer noch ganze Zahlen sind und keine Binärzahlen. Binärzahlen sind wie Hexadezimal oder Dezimal nur eine Repräsentation von Integern.
Ja, ist klar. Aber inwiefern ich durch Dual-Objekte Probleme habe, die eine dual() oder bin()-Funktion nicht mit sich bringt, ist mir nicht klar.

Ein

Code: Alles auswählen

number = int(number)
kann ich natürlich in meinen Entwurf auch einbauen; es "löst" das Problem des Umgangs mit den floats. Aber für sinnvoll halte ich das nicht - habe ich ja in einem früheren Posting schon erläutert. Dann merke ich doch gar nicht, dass ein Dual(13.6) oder bin(13.6) oder wie auch immer gar nicht das macht, was ich vermutlich denke, dass es macht. Aber m.E. ist das auch gar nicht der Punkt.

Ich bin - für mich - immer noch nicht "fertig" mit meinem Entwurf.

Was müsste ich den konkret ändern, um duck-typing zu realisieren?
Geht das nur mit einem komplett anderen Ansatz oder sind es nur ein paar Änderungen an der __init__()-Methode?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

EyDu hat geschrieben:..., aber da ich nicht weiss was pütone vor hat, hoffe ich einfach mal, dass es Sinn ergibt.
Ach, gar nichts Großes. Hin und wieder muss ich mit ein paar Dualzahlen rechnen und außerdem bin ich dabei, nicht nur irgendwie in Python zu programmieren, sondern das auch möglichst "pythonic" zu tun. Da kam das gerade recht, weil es klein und überschaubar ist und man eben auch sinnvoll - ganz sicher nicht zwingend - Klassen/Objekte einsetzen kann.

Ich finde es nett, wenn z.B. sowas geht:

Code: Alles auswählen

>>> from dual import *
>>> a = Dual(23)
>>> b = Dual('110101')
>>> a+b
1001100
>>> a*b
10011000011

@zap zu 2)

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Hier mal der pythonischere Weg der Initialisierung:

Code: Alles auswählen

class Dual(object):

    def __init__(self,value):
        self.value = value

    @classmethod
    def fromstring(cls, value):
        return cls(
            int(value,2))

    def __str__(self):
        sgn = "-" if self.value<0 else ""
        bin = "" if self.value!=0 else "0"
        dec = abs(self.value)
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __add__(self,bin):
        return Dual(self.value+bin.value)

    def __sub__(self,bin):
        return Dual(self.value-bin.value)

    def __mul__(self,bin):
        return Dual(self.value*bin.value)

    def __div__(self,bin):
        return Dual(self.value//bin.value)

    def __mod__(self,bin):
        return Dual(self.value%bin.value)

    def __rep__(self):
        return "<Dual %s-%d>" % (str(self), self.value)

if __name__ == "__main__":
    z1 = Dual.fromstring("1001")
    z2 = Dual(5)
    print "%s + %s = %s" %(z1,z2,z1+z2)
    print "%s - %s = %s" %(z1,z2,z1-z2)
    print "%s * %s = %s" %(z1,z2,z1*z2)
    print "%s / %s = %s" %(z1,z2,z1/z2)
    print "%s %% %s = %s" %(z1,z2,z1%z2)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

def __repr__(self):
    return "Dual(%s)" % self.value
Dann tut die Methode auch das, wozu sie gedacht ist.
BlackJack

Mein Vorschlag zum Thema:

Code: Alles auswählen

import operator

class Dual(object):
    def __init__(self, value):
        if isinstance(value, basestring):
            value = int(value, 2)
        self.value = int(value)
    
    def __int__(self):
        return self.value
    
    def __str__(self):
        value = int(self)
        sign = '-' if value < 0 else ''
        value = abs(value)
        result = list()
        while value:
            result.append(str(value & 1))
            value >>= 1
        return sign + ''.join(reversed(result))
    
    def __repr__(self):
        return '<Dual %s:%d>' % (self, self)


def make_binary_method(cls, operation):
    def meth(self, other):
        return cls(operation(self.value, int(other)))
    return meth

for operation in ['add', 'sub', 'mul', 'div', 'truediv', 'mod']:
    setattr(Dual,
            '__%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))
    setattr(Dual,
            '__r%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))


def main():
    z1 = Dual(13)
    z2 = Dual(5)
    print "%s + %s = %s" % (z1, z2, z1 + z2)
    print "%s - %s = %s" % (z1, z2, z1 - z2)
    print "%s * %s = %s" % (z1, z2, z1 * z2)
    print "%s / %s = %s" % (z1, z2, z1 / z2)
    print "%s %% %s = %s" % (z1, z2, z1 % z2)
    print repr(1 + Dual(15))

if __name__ == '__main__':
    main()
Nimmt nicht nur in der `__init__()` alles an, was man nach `int` Konvertieren lässt, sondern lässt sich auch mit solchen Objekten in Rechnungen verwenden. Zum Beispiel mit `int`\s.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@audax
Das ist jetzt mal Code, den auch ich verstehe ... :D

Danke, war wirklich hilfreich. Wirft bei mir natürlich gleich weitere Fragen auf:

Warum

Code: Alles auswählen

@classmethod
def fromstring(cls, value):
    return cls(int(value,2))
statt z.B.

Code: Alles auswählen

@staticmethod
def fromstring(value):
    return Dual(int(value,2))
Und: Wenn das "Python thinking" ist, okay. Macht mich aber ein bisschen traurig, weil ich es viel netter fänd, wenn Dual("1010") ebenso möglich wäre wie Dual(15).

@BlackJack
Danke auch für den Entwurf. Den mittleren Teil - Zeile 26-37 - verstehe ich im Moment noch nicht, aber das ist vorerst nicht weiter schlimm; bei nächster Gelegenheit arbeite ich mich dadurch.

Jetzt bin ich natürlich völlig verwirrt: audax will eine extra Methode zur Erzeugung von Dual-Objekten aus einem "dualen String", du machst es nun an der Stelle so ähnlich wie ich in meinem ersten Entwurf und verwendest auch isinstance() :?:

Was mich immer noch nicht glücklich macht:
Bei audax' Variante führt die Erzeugung eines Dual-Objekts aus einem float zunächst nicht zu einem Fehler; erst beim Rechnen oder Ausgeben kommt die Fehlermeldung.
Bei BlackJacks Variante wird wiederum der float kommentarlos zu einem int und dann weiter verarbeitet.

Richtig toll ist natürlich, dass man mit BlackJacks Version jetzt auch int's mit Dual's verrechnen kann. Das gefällt mir.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

pütone hat geschrieben:

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'
:oops: Hatte nen "dreher" in den Rückgabewerten, habs oben geändert.
BlackJack

@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.

Die Zeilen 26-37 erzeugen die ganzen Rechenmethoden in der Liste programmatisch. Hätte man auch mit "Copy'n'Paste"-Programmierung machen können.

Ich behandle Zeichenketten in der `__init__()` besonders weil das IMHO nicht ganz unpythonisch ist. Man sieht das jedenfalls häufiger. Das ist halt ein "besonderer" Datentyp.

Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.
Dachte, ich hätte es verstanden. Wollte es ausprobieren. Und zwar so:

Code: Alles auswählen

>>> from dual import *
>>> class MyDual(Dual):
...     pass
...
>>> MyDual.classmeth('10101')
<Dual 10101-21>
>>> MyDual.staticmeth('10101')
<Dual 10101-21>
Dann habe ich es doch nicht verstanden.
BlackJack hat geschrieben:Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)
Grausam :x
BlackJack

@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)
Jaaaah! :idea: :idea: :idea:
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

@Blackjack:
Exakt die Lösung ist mir vorhin auch eingefallen :D
Brauchte sowas, um dummy-Klassen für Tests zu generieren. Python ist schon recht flexibel :]

Und...basestring kannte ich noch nicht. Sehr pratkisch!
BlackJack

@rabit: Um noch eine konkrete Antwort auf die Frage nach der Binärdarstellung von 2.35 zu liefern: 10.010110₂, wobei der unterstrichene Teil die Periode ist. Der Dezimalbruch lässt sich binär nicht mit endlichen Stellen darstellen.
Antworten