Mittwoch, 8. Oktober 2014

 

Musterlösung zu Unit-Tests

Musterlösung zur Aufgabe zu Unit-Tests

Folgende Grenzfälle werden getestet:
Sonderfälle:
"Normalfälle":
Weitere Annahmen, die aus der Angabe nicht unbedingt hervorgehen:
Die Artikel (Elemente/Items) werden in der Reihenfolge und der gegebenen Anzahl gespeichert, als addItems() aufgerufen wurde. Wird ein Element mit der Anzahl 5 in den Warenkorb aufgenommen, so wird das Element 5 mal gespeichert.

Testklasse:
Die Kommentare wurden für diesen Beitrag weggelassen, da die Klasse ohnedies schon sehr lange ist.
import java.util.Iterator;
import junit.framework.TestCase;

public class ShoppingCartTest extends TestCase {

    private ShoppingCart cart;
    private ItemCount itemCount;

    private Item[] items = { new Item(1), new Item(2), new Item(3) };

    protected void setUp() throws Exception {
        cart = new MyCart();
        itemCount = (ItemCount) cart;
    }

    protected void tearDown() throws Exception {
    }

    public void testAddItems0() {
        try {
            cart.addItems(items[0], 0);
            assertEquals("testDeleteItems0: 0 Elemente", 0, cart.itemCount());
        } catch (NegativeCountException e) {
            fail("testAddItem0: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        }
    }

    public void testAddItemNegativ() {
        try {
            cart.addItems(items[0], -23);
            fail("testAddItemNegativ: es müsste eine NegativeCountException"
                 + " geworfen werden");
        } catch (NegativeCountException e) {
        }
    }

    public void testDeleteItems0() {
        try {
            cart.addItems(items[0], 1);
            cart.deleteItems(items[0], 0);
            assertEquals("testDeleteItems0: 1 Element", 1, cart.itemCount());
        } catch (NegativeCountException e) {
            fail("testDeleteItems0: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        } catch (NoSuchItemException e) {
            fail("testDeleteItems0: es dürfte keine NoSuchItemException"
                 + " geworfen werden");
        }
    }

    public void testDeleteItemsNoSuchItem() {
        try {
            cart.addItems(items[0], 1);
            cart.deleteItems(items[1], 1);
            fail("testDeleteItemsNoSuchItem: es müsste NoSuchItemException"
                 + " geworfen werden");
        } catch (NegativeCountException e) {
            fail("testDeleteItemsNoSuchItem: es dürfte keine" 
                 + " NegativeCountException geworfen werden");
        } catch (NoSuchItemException e) {
            assertEquals("testDeleteItemsNoSuchItem: 1 Element", 1,
                    cart.itemCount());
        }
    }

    public void testDeleteItemNegativ() {
        try {
            cart.addItems(items[0], 2);
            cart.deleteItems(items[0], -23);
            fail("testAddItemNegativ: es müsste eine NegativeCountException"
                 + " geworfen werden");
        } catch (NegativeCountException e) {
        } catch (NoSuchItemException e) {
            fail("testAddItemNegativ: es dürfte keine NoSuchItemException"
                 + " geworfen werden");
  }
    }

    public void testAddItems10() {
        try {
            cart.addItems(items[0], 3);
            cart.addItems(items[1], 3);
            cart.addItems(items[2], 4);
            assertEquals("testAddItems10: 10 Elemente", 10, cart.itemCount());
        } catch (NegativeCountException e) {
            fail("testAddItems10: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        }
    }

    public void testAddDeleteItems5() {
        try {
            cart.addItems(items[0], 3);
            cart.addItems(items[1], 3);
            cart.addItems(items[2], 4);
            cart.deleteItems(items[0], 2);
            cart.deleteItems(items[2], 2);
            cart.deleteItems(items[1], 1);
            assertEquals("testAddDeleteItems5: 5 Elemente", 5, cart.itemCount());
        } catch (NegativeCountException e) {
            fail("testAddDeleteItems5: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        } catch (NoSuchItemException e) {
            fail("testAddDeleteItems5: es dürfte keine NoSuchItemException"
                 + " geworfen werden");
        }
    }

    public void testIterator() {
        try {
            cart.addItems(items[0], 3);
            cart.addItems(items[1], 3);
            cart.addItems(items[2], 4);
            Iterator<Item> iterator = cart.iterator();
            int cntItems = 0;
            Item[] stroredItems = new Item[10];
            while (iterator.hasNext()) {
                stroredItems[cntItems] = iterator.next();
                cntItems++;
            }
            assertEquals("testIterator: 10 Elemente", 10, cntItems);
            for (int i = 0; i < 3; i++) {
                assertEquals("testIterator: Element sollte gleich sein",
                        items[0], stroredItems[i]);
                System.out.println(stroredItems[i]);
            }
            for (int i = 3; i < 6; i++) {
                assertEquals("testIterator: Element sollte gleich sein",
                        items[1], stroredItems[i]);
                System.out.println(stroredItems[i]);
            }
            for (int i = 6; i < 10; i++) {
                assertEquals("testIterator: Element sollte gleich sein",
                        items[2], stroredItems[i]);
                System.out.println(stroredItems[i]);
            }
        } catch (NegativeCountException e) {
            fail("testIterator: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        }
    }

    public void testItemCount() {
        try {
            cart.addItems(items[0], 3);
            cart.addItems(items[1], 3);
            cart.addItems(items[0], 1);
            cart.addItems(items[2], 4);
            cart.addItems(items[0], 2);
            assertEquals("testItemCount: Anzahl falsch", 6,
                    itemCount.itemCount(items[0]));
            assertEquals("testItemCount: Anzahl falsch", 3,
                    itemCount.itemCount(items[1]));
            assertEquals("testItemCount: Anzahl falsch", 4,
                    itemCount.itemCount(items[2]));
        } catch (NegativeCountException e) {
            fail("testItemCount: es dürfte keine NegativeCountException"
                 + " geworfen werden");
        } catch (NoSuchItemException e) {
            fail("testItemCount: es dürfte keine NoSuchItemException"
                 + " geworfen werden");
        }
    }

    public void testItemCountNosuchItem() {
        @SuppressWarnings("unused")
        int cnt;
        try {
            cnt = itemCount.itemCount(items[0]);
            fail("testItemCountNosuchItem: es müsste eine NoSuchItemException"
                 + " geworfen werden");
        } catch (NoSuchItemException e) {
        }
        try {
            cart.addItems(items[1], 3);
            cnt = itemCount.itemCount(items[0]);
            fail("testItemCountNosuchItem: es müsste eine NoSuchItemException"
                 + " geworfen werden");
        } catch (NegativeCountException e) {
            fail("testItemCountNosuchItem: es dürfte keine"
                 + " NegativeCountException geworfen werden");
        } catch (NoSuchItemException e) {
        }
    }
}

Implementierung des Interfaces ShoppingCart:
Zusätzlich zur originalen Aufgabenstellung wurde noch folgendes Interface implementiert, damit man einfach die Anzahl eines bestimmten Items bestimmen kann:
public interface ItemCount {
    public int itemCount(Item anItem) throws NoSuchItemException;
}

Zur Speicherung der Items wird einfach eine ArrayList verwendet.
import java.util.ArrayList;
import java.util.Iterator;

public class MyCart implements ShoppingCart, ItemCount {

    private ArrayList<Item> items = new ArrayList<Item>();

    public void addItems(Item anItem, int quantity)
            throws NegativeCountException {
        if (quantity < 0) {
            throw new NegativeCountException();
        }
        for (int i = 0; i < quantity; i++) {
            items.add(anItem);
        }
    }

    public void deleteItems(Item anItem, int quantity)
            throws NegativeCountException, NoSuchItemException {
        if (quantity < 0) {
            throw new NegativeCountException();
        }
        for (int i = 0; i < quantity; i++) {
            if (!items.remove(anItem))
                throw new NoSuchItemException();
        }
    }

    public int itemCount() {
        return items.size();
    }

    public Iterator<Item> iterator() {
        return items.iterator();
    }

    @Override
    public int itemCount(Item anItem) throws NoSuchItemException {
        int cnt = 0;
        if (!items.contains(anItem)) {
            throw new NoSuchItemException();
        }
        for (Item item : items) {
            if (item == anItem) {
                cnt++;
            }
        }
        return cnt;
    }

}
Weiters müssen natürlich die geforderten Exceptions implementiert werden (Kommentare wurden auch hier weggelassen):
public class NegativeCountException extends Exception {
}
public class NoSuchItemException extends Exception {
}

Die Klasse für die Artikel (Item) wurde einfach durch eine Nummer ergänzt, die beim Konstruktor angegeben werden muss. Für eine realistische Anwendung müsste diese Klasse noch erweitert werden:
public class Item {
  private int itemNr = 0;

  public Item(int itemNr) {
    this.itemNr = itemNr;
  }

  @Override
  public String toString() {
    return "Item " + itemNr;
  }
}

Labels: , , ,


Montag, 18. November 2013

 

Musterlösung Römische Zahlen umwandeln (POS1: 2BHIF)

Mögliche Lösung zu Römische Zahlen umwandeln (POS1: 2BHIF).

Die Klasse könnte folgendermaßen programmiert werden. Das Dictionary enthält die möglichen "Ziffern" (unter Hochkomma, da bestimmte Ziffernkombinationen, wie zum Beispiel "IX", als eine Ziffer gelten). Zur Umrechnung müssen die Werte absteigend sortiert werden, damit die richtige "Ziffer" (z.B. "IX" für 9) verwendet wird.

Bei der Umrechnung in römische Zahlen wird der Reihe nach von der größten zur kleinsten "Ziffer" (daher absteigend sortiert) probiert, ob der Wert enthalten ist. Ist das der Fall, dann wird die "Ziffer" an den String angehängt und der entsprechende Wert von der Zahl abgezogen. Zum Beispiel 1999

1999 M
999 IM
0 ferig
oder 392
392 C
292 C
192 C
92 XC
2 I
1 I
0 fertig

Bei der umgekehrten Umrechnung wird der Reihe nach probiert, ob am Beginn der römischen Zahl eine bestimmte "Ziffer" vorkommt. Ist dies der Fall, so wird der entsprechende Wert zum Ergebnis dazugezählt. Mit einem Zähler wird geprüft, dass keine Ziffer mehr als drei mal vorkommt. Zum Beispiel CCCXCII

CCCXCII 100 (C)
CCXCII 200 (C)
CXCII 300 (C)
XCII 390 (XC)
II 391 (I)
I 392 (I)

Zum Testen wird einfach eine Liste von Zahlen verwendet, die in römische Zahlen und wieder zurück gerechnet werden. Ungültige Zahlen liefern eine Exception, die bei einem entsprechenden Testfall kommen muss:

    # test value error
    try:
        ra = RomanNumber("VIIII")
        assert(False)
    except ValueError:
        pass

Die gesamte Klasse könnte also so aussehen:

#!/usr/bin/env python3
"""
 File:    RomanNumber.py
 Desc:    class for roman numbers
 Created: 2013-11-18, Harald R. Haberstroh
"""
class RomanNumber:
    """Roman numbers.
    Internal representation is always the integer value.
    This implementation stores both values (integer and roman).
    """
    digits = { "I":1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000,
               "IV":4, "IX":9, "XL":40, "XC":90, "CD":400, "CM":900}
    keys = list(digits.items())
    keys.sort(key=lambda x: -x[1])

    def __init__(self, value):
        """Initialize with value.
        If type of value is int, than this is the integer value.
        If type of value is str, than this is a roman number, which has
        to be converted into the internal int-value"""
        if type(value) is str:
            self.roman = value
            self.intval = self.__toInt(value)
        else:
            self.intval = value
            self.roman = self.__toRoman(value)

    def toRoman(self):
        """returns string with roman representation"""
        return self.roman

    def toInt(self):
        """returns integer value of roman number"""
        return self.intval

    def add(self, other):
        """add the value of other (RomanNumber) to own (self) value and
        return a new RomanNumber object."""
        return RomanNumber(self.intval + other.toInt())

    def sub(self, other):
        """subtract the value of other (RomanNumber) from own (self) value and
        return a new RomanNumber object."""
        return RomanNumber(self.intval - other.toInt())

    def mul(self, other):
        """multiply the value of other (RomanNumber) with own (self) value and
        return a new RomanNumber object."""
        return RomanNumber(self.intval * other.toInt())

    def div(self, other):
        """divide (integer division) own value by the value of other value and
        return a new RomanNumber object."""
        return RomanNumber(self.intval // other.toInt())

    def __str__(self):
        """returns the same value as toRoman(self)"""
        return "%s (%d)" % (self.roman, self.intval)

    def __toRoman(self, intval):
        romval = ""
        for digit, val in self.keys:
            while intval >= val:
                romval += digit
                intval -= val
        return romval

    def __toInt(self, romval):
        roman = romval.upper()
        intval = 0
        for digit, val in self.keys:
            cntDigit = 0 # count same group
            while roman.startswith(digit):
                intval += val
                roman = roman[len(digit):]
                cntDigit += 1
                if cntDigit >= 4:
                    raise ValueError()
        return intval

if __name__ == '__main__':
    zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 32, 43, 48, 49, 54, 65, 76, 87, 98,
          99, 100, 201, 302, 403, 491, 499, 504, 605, 706, 807, 908, 999, 1001,
          1279, 1331, 1496, 1883, 1949, 1967, 1990, 1999, 2006, 2013]

    for f in zahlen:
        rn = RomanNumber(f)
#        print('%4d: %20s - %4d' % (f, rn.toRoman(), rn.toInt()))
        assert(f == rn.toInt())

    for i in range(len(zahlen) // 2):
        ra = RomanNumber(zahlen[i])
        rb = RomanNumber(zahlen[-i])
        rc = ra.add(rb)
        print(ra, "+", rb, "=", rc)
        assert(zahlen[i] + zahlen[-i] == rc.toInt())
        rc = rb.sub(ra)
        print(rb, "-", ra, "=", rc)
        assert(zahlen[-i] - zahlen[i] == rc.toInt())
    for i in range(len(zahlen) // 8):
        ra = RomanNumber(zahlen[i])
        rb = RomanNumber(zahlen[len(zahlen) // 2 - i])
        rc = ra.mul(rb)
        print(ra, "*", rb, "=", rc)
        assert(zahlen[i] * zahlen[len(zahlen) // 2 - i] == rc.toInt())

    # test value error
    try:
        ra = RomanNumber("VIIII")
        assert(False)
    except ValueError:
        pass
    print("all tests passed")

Beim romancalc.py wird jeder Schritt mit einem try - except - Block umgeben, um die Fehler- und Sonderfälle zu behandeln.

Eine Spezialität ist der Aufruf des entsprechenden Operators. Hier wird ein Dictionary mit den möglichen Operationen verwendet, wobei der Aufruf als Funktion und nicht als Methode stattfindet (der erste Parameter self enthält ja das Objekt, welches dann explizit mit übergeben wird).

Das gesamte Modul könnte wie folgt aussehen:

#!/usr/bin/env python3
"""
 File:    romancalc.py
 Desc:    simple caluculator for roman numbers, using RomanNumber
 Created: 2013-11-18, Harald R. Haberstroh
"""
from RomanNumber import RomanNumber
import sys

operators = { "+":RomanNumber.add, "-":RomanNumber.sub,
              "*":RomanNumber.mul, "/":RomanNumber.div }
while True:
    try:
        line = input(">>> ")
    except EOFError:
        break
    try:
        a, op, b = line.split()
    except ValueError:
        print("syntax error", file=sys.stderr)
        continue
    if a.isdigit():
        a = int(a)
    if b.isdigit():
        b = int(b)
    try:
        ra = RomanNumber(a)
        rb = RomanNumber(b)
    except ValueError:
        print("invalid roman number", file=sys.stderr)
        continue
    try:
        rc = operators[op](ra, rb)
        print(ra, op, rb, "=", rc)
    except KeyError:
        print("invalid operator '%s'" % (op), file=sys.stderr)

Labels: , , ,


Mittwoch, 9. Oktober 2013

 

Musterlösung zu Aufgabe 2 "Denksport mit Python, erste Anwendung von Mercurial" (POS1: 2BHIF)

Mögliche Lösungen zur Aufgabe 2 "Denksport mit Python, erste Anwendung von Mercurial sind (nur die Python-Scrips):
#!/usr/bin/env python3
"""
 File:    pythagorean_triple.py
 Desc:    Ein Programm zur Bestimmung eines pythagoräischen Tripel (a2+b2=c2
          für a < b < c), wobei a+b+c == 1000.
 Created: 2013-09-23, Harald R. Haberstroh
"""
from sys import argv
from math import sqrt


if __name__ == '__main__':
    summe = 1000
    if len(argv) == 2:
        summe = int(argv[1])
    for a in range(1, summe // 3):
        for b in range(a + 1, summe // 2):
            a2 = a * a
            b2 = b * b
            c2 = a2 + b2
            c = sqrt(c2)
            if a < b < c and a + b + c == summe:
                print(a, b, int(c))
                print(int(a * b * c))
#!/usr/bin/env python3
"""
 File:    sumdigits2pow1000.py
 Desc:    calculate the sum of the digits of 2**1000 (exponent can be changed
          with commandline argument)
 Created: 2013-09-23, Harald R. Haberstroh
"""

from sys import argv

if __name__ == '__main__':
    if len(argv) == 2:
        exponent = int(argv[1])
    else:
        exponent = 1000
    print(sum(list(map(int, list(str(2 ** exponent))))))
#!/usr/bin/env python3
"""
 File:    sumdigitsfact.py
 Desc:    calculate the sum of the digits of 100! (100 can be changed
          with commandline argument)
 Created: 2013-09-23, Harald R. Haberstroh
"""

from sys import argv

def fact(n):
    """n!"""
    f = 1
    for i in range(1, n + 1):
        f *= i
    return f

if __name__ == '__main__':
    if len(argv) == 2:
        n = int(argv[1])
    else:
        n = 100
    print(sum(list(map(int, list(str(fact(n)))))))

Die Palindrom-Beispiele sind ganz ähnlich und wurden daher in ein Programm gepackt, bei dem man als Parameter auf der Kommandozeile die Anzahl der Stellen angeben kann (4, 6, 8). Zusätzlich kann noch '-l' angegeben werden, um eine Liste der Produkte zu erhalten. Die Ergebnisliste ist nicht ganz vollständig, denn es wird das Ergebnis des Produkts als Schlüssel verwendet. Damit gehen Lösungen wie zum Beispiel 111111 = 143 * 777 = 231 * 481 = 259 * 429 verloren.
#!/usr/bin/env python3
"""
 File:    palindrome.py
 Desc:    largest palindrome of product of two n/2-digit numbers with n digits
 Created: 2013-09-23, Harald R. Haberstroh
"""

from sys import argv

def checkPalindrom(s):
    """
    checks whether string is a palindrome
    """
    l = len(s)
    for i in range(l // 2):
        if s[i] != s[l - i - 1]:
            return False
    return True

def genPalindrom(n):
    """
    build palindrome out of products
    """
    # Dictionary only for list of palindromes not for maximum
    palindromes = {}
    products = []
    maxPalindrome = 0
    maxPalindromeFactors = ""
    for i in range(10**((n - 1) // 2), 10 ** (n // 2)):
        for j in range(10**((n - 1) // 2), 10 ** (n // 2)):
            product = str(i * j)
            if checkPalindrom(product):
                if not product in palindromes:
                    palindromes[product] = str(i) + " * " + str(j) + " = " + product
                    products.append(product)
                    if int(product) > maxPalindrome:
                        maxPalindrome = int(product)
                        maxPalindromeFactors = str(i) + " * " + str(j) + " = " + product
    return maxPalindromeFactors, palindromes

if __name__ == '__main__':
    n = 2
    if len(argv) > 1:
        n = int(argv[1])
    maxPalindrome, palindromes = genPalindrom(n)
    if "-l" in argv:
        lstPalindromes = list(palindromes.values())
        lstPalindromes.sort()
        for palindrome in lstPalindromes:
            print(palindrome)
    print("largest palindrome: %s" % maxPalindrome)

Labels: , ,


 

Musterlösung zu Wiederholung Python (POS1: 2BHIF)

Eine mögliche Lösung zur Aufgabe Wiederholung Python (POS1: 2BHIF) ist folgende:
"""
text_analyzer.py
Task 1: Repeat previous material (1st class)
2012-09-10, Harald R. Haberstroh (hp@htlwrn.ac.at)
"""


def input_sentences():
    '''returns al list of sentences/lines'''
    lst = []
    eof = False
    while not eof:
        try:
            line = input("Satz: ")
            lst.append(line)
        except EOFError:
            eof = True
    return lst


def _remove_punctuation(string):
    '''helper removes all punctuation marks within string'''
    for punctuation in '''"',;.:-''':
        string = string.replace(punctuation, " ")
    return string


def split_sentences(lst):
    '''split a list (lst) of sentences into list of list of words'''
    wordlist = []
    for sentence in lst:
        wordlist.append(_remove_punctuation(sentence).split())
    return wordlist


def purge_bad_words(lst):
    '''remove all bad words (length less than 2 chars, words
    containing non alphabetic chars)
    '''
    for sublist in lst:
        idx = 0
        while idx < len(sublist):
            if not len(sublist[idx]) >= 2 or not sublist[idx].isalpha():
                del(sublist[idx])
            else:
                idx += 1
    return lst


def analyze_words(lst):
    '''returns a dictionary of words (with the number of occurrences)
    of the list of list of words'''
    dict = {}
    for sublist in lst:
        for word in sublist:
            word = word.lower()
            if word in dict:
                dict[word] += 1
            else:
                dict[word] = 1
    return dict

def analyze_letters(lst):
    '''returns a dictionary of letters (with the number of occurrences)
    of the list of list of words'''
    dict = {}
    for sublist in lst:
        for word in sublist:
            for char in word.lower():
                if char in dict:
                    dict[char] += 1
                else:
                    dict[char] = 1
    return dict
                

def purge_analyzed_letters(dic):
    '''remove umlauts'''
    for umlaut in "äöüß":
        if umlaut in dic:
            del(dic[umlaut])
    return dic


def _key(tupl):
    '''returns 2nd element of tuple for sorting'''
    return tupl[1]


def sort_letters(dic):
    '''returns a sorted list of tuples of letters, sorted by
    number of letters'''
    tuples = list(dic.items())
    tuples.sort(key=_key, reverse=True)
    return tuples


def test_input():
    '''returns a fixed list of sentences for testing purposes'''
    return ['Maxi und Mini verliefen sich im Wald, aber bald.',
        '10 alte Fledermäuse flogen im Wald',
        'Hat die alte Meisterhexe...',
        "Seid's gewesen. Denn als Geister...",
        'Erst hervor der alte Meister.']


if __name__ == '__main__':
    #lst = input_sentences()
    lst = test_input()
    
    print('split_sentences(lst)')
    wordlist = split_sentences(lst)
    print(wordlist)
    
    print('\npurge_bad_words(wordlist)')
    wordlist = purge_bad_words(wordlist)
    print(wordlist)

    print('\nanalyze_words(wordlist)')
    print(analyze_words(wordlist))

    print('\nanalyze_letters(wordlist)')
    dic = analyze_letters(wordlist)
    print(dic)

    print('\npurge_analyzed_letters(dic)')
    dic = purge_analyzed_letters(dic)
    print(dic)

    print('\nsort_letters(dic)')
    sorted_letters = sort_letters(dic)
    print(sorted_letters)

    print('\n\n*** task 9 ***\n')
    print('''"abcdef".index("c")''') # ValueError: substring not found
    print('''ValueError: substring not found''')
    print('''"abcdef".index("g")''') # ValueError: substring not found
    print('''ValueError: substring not found''')
    print('''"abcdef".index("de")''')
    print("abcdef".index("de"))
    print('''"c" in "abcdef" ''')
    print("c" in "abcdef")
    print('''"g" in "abcdef" ''')
    print("g" in "abcdef")
    print('''"de" in "abcdef" ''')
    print("de" in "abcdef")
    print('''"abcdef"[2:4]''')
    print("abcdef"[2:4])
    print('''"abcdef"[2:]''')
    print("abcdef"[2:])
    print('''"abcdef"[-1]''')
    print("abcdef"[-1])
    print('''"abcdef"[2:-1]''')
    print("abcdef"[2:-1])
    print('''[1, 2, 3, 4, 5].index(3)''')
    print([1, 2, 3, 4, 5].index(3))
    print('''[1, 2, 3, 4, 5].index(9)''') # ValueError: 9 is not in list
    print('''ValueError: 9 is not in list''')
    print('''[1, 2, 3, 4, 5][5]''') # IndexError: list index out of range
    print('''IndexError: list index out of range''')
    print('''[1, 2, 3, 4, 5][2:4]''')
    print([1, 2, 3, 4, 5][2:4])
    print('''[1, 2, 3, 4, 5][-2]''')
    print([1, 2, 3, 4, 5][-2])
    print('''[1, 2, 3, 4, 5][5:9]''')
    print([1, 2, 3, 4, 5][5:9])
    print('''[1, 2, 3, 4, 5][4:]''')
    print([1, 2, 3, 4, 5][4:])
    print('''len("abc") + len(range(3)) + len({1, 2, 3}) + len({1: 2, 2: 3})''')
    print(len("abc") + len(range(3)) + len({1, 2, 3}) + len({1: 2, 2: 3}))

Labels: , ,


Donnerstag, 4. Juli 2013

 

XML Datei aus einer ZIP-Datei mit Python ändern und wieder zurückschreiben (Hack für Probleme mit K3B und diversen MP3-Playern)

Der Titel sagt's bereits. Dieses kleine Script ändert den Inhalt einer XML-Datei in einer ZIP-Datei. Konkret handelt es sich um eine Projektdatei des Linux-Brennprogrammes k3b. Ich erstelle damit CDs für's Autoradio mit MP3s. Allerdings enthält die CD die Dateien in alphabetischer Reihenfolge und das Autoradio spielt das in der Reihenfolge ab. Ich habe keinen anderen Weg gefunden, als die Dateinamen entsprechend zu ändern. Das ist natürlich in einer grafischen Oberfläche mühsam.

Mit k3b erzeuge ich ein neues Projekt, in dem ich die Songs in der Reihenfolge, die ich haben will hinzufüge. Dann speichere ich das Projekt und lasse die Dateinamen durch das Script in der Projektdatei ändern. Dann lade ich das Projekt und brenne die CD. Die Lieder sind dann in der richtigen Reihenfolge, weil die Namen mit einer Nummer wie hier 001 - Lied.mp3 versehen werden.

#!/usr/bin/env python
"""
change entries so that they are sorted by name used with k3b-data CDs (mp3
CDs).

Procedure:
    1. make k3p project and add files.
    2. save this project
    3. close the project (leave k3b open)
    4. run this script: numbermymp3.py your_project_name.k3p
    4. load the project again.
    5. as you can see, the files ar starting with 3-digit numbers preserving
       the original order when adding the files.

2013-07-04, Harald R. Haberstroh
"""
import sys
import codecs
import zipfile
from xml.dom import minidom

def getfiles(zipfilename):
    """get dictionary filename:data with all files in zipfile"""
    zf = zipfile.ZipFile(zipfilename)
    files = {}
    for info in zf.infolist():
        files[info.filename] = zf.read(info.filename)
    return files

def process(xmlstr):
    """process xml with file entries and change 'name' attribute according
    to number of file (starting with 001)."""
    xmldoc = minidom.parseString(xmlstr)
    names = xmldoc.getElementsByTagName('file')
    num = 1
    for name in names:
        numstr = "%03d" % num
        name.attributes['name'].value = numstr + ' - ' + \
                name.attributes['name'].value
        num += 1
    return xmldoc.toprettyxml().encode('utf-8')

def genzipfile(zipfilename, files):
    """generate new zipfile (k3b-file) with dict 'files' which has file name as
    key and the content as data"""
    zf = zipfile.ZipFile(zipfilename, mode='w')
    try:
        for name in files:
            zf.writestr(name, files[name])
    finally:
        zf.close()

def main():
    """all to gether now"""
    if len(sys.argv) != 2:
        print "usage: " + sys.argv[0] + " k3b_project_name.k3b"
        print "renames files so that the order is the same as in the project"
        sys.exit(1)
    fname = sys.argv[1]
    files = getfiles(fname)
    text = process(files['maindata.xml'])
    files['maindata.xml'] = text
    genzipfile(fname, files)

if __name__ == '__main__':
    main()

Labels: ,


Donnerstag, 10. März 2011

 

Lösungen zu "Übungsbeispiele zu Sequenzen, Schleifen und Funktionen in Python" (POS1: 1B)

Hier mögliche Lösungen zu der Aufgabe "Übungsbeispiele zu Sequenzen, Schleifen und Funktionen in Python (POS1: 1B)". Einige der Aufgaben enthalten Suchaufgaben. Die Funktionen dazu sind teilweise nicht "strukturiert" programmiert, das heißt, dass sie in diesen Fällen nicht nur ein return sondern mehrere Ausgänge haben. Um das zu verdeutlichen, beachten Sie folgendes Beispiel:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            for c in string[1:]:
                if c < '0' or c > '9':
                    return False
        else:
            for c in string:
                if c < '0' or c > '9':
                    return False
    else:
        return False
    return True
Hier erfolgt ein return False aus den Schleifen über den String string, sobald ein Fehler gefunden wurde. Das vereinfacht den Algorithmus, widerspricht aber der strukturierten Programmierung, die bei Funkionen nur einen Ausgang erlaubt (bei Schleifen gilt das auch, die Schleife verlässt man nur durch die Schleifenbedingung, nicht durch break oder eben return). Beim Debuggen größerer Systeme ist es von Vorteil, wenn man sich darauf verlassen kann, dass jede Funktion nur einen Ausgang hat, denn man braucht dann nur diesen einen beobachten und nicht mehrere.
Bei obigem Beispiel müsste man while-Schleifen verwenden:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    ok = True
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            i = 1
            while ok and i < len(string):
                if string[i] < '0' or string[i] > '9':
                    ok = False
                i += 1
        else:
            i = 0
            while ok and i < len(string):
                if string[i] < '0' or string[i] > '9':
                    ok = False
                i += 1
    else:
        ok = False
    return ok
Diese Variante könne man noch "vereinfachen", weil sich die beiden Zweige mit oder ohne Vorzeichen nur im Startwert der Laufvariable i unterscheiden:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    ok = True
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            i = 1
        else:
            i = 0
        while ok and i < len(string):
            if string[i] < '0' or string[i] > '9':
                ok = False
            i += 1
    else:
        ok = False
    return ok
Die "nicht strukturierte" Variante ist aber bei weitem schneller entwickelt und durch die automatischen Tests ist auch sichergestellt, dass sie funktioniert.

Die Tests verwenden assert(), um die Ergebnisse mit den erwarteten Ergebnissen zu vergleichen. assert() bricht das Programm auf der Stelle ab, wenn die Bedingung nicht erfüllt ist. Dadurch hat man die Möglichkeit, den Fehler einzugrenzen. Passt die Bedingung, so läuft das Programm einfach weiter.

"""
author: Haberstroh, Harald
matnr:  d09666
file:   sequenzenbeispiele.py
desc:   Beispiele einfacher Funktionen mit Sequenzen (vgl. Programmierblog)
        Einige Funktionen bauen auf den anderen auf.
        
        Zu jeder Funktion (ausgenommen Hilfsfunktion) gibt es eine Testfunktion
        die die jeweilige Funktion mit Testwerten prüft.
        
        Die Funktion assert() prüft den Wahrheitswert des Parameters. Ist dieser
        True, so läuft das Programm einfach weiter. Ist er jedoch False, wird
        das Programm mit einer Fehlermeldung und Zeilennummer abgebrochen.
        Man kann dadurch feststellen, welcher Test nicht funktioniert hat.
date:   7.3.2011
class:  Lehrer
catnr:  33
"""
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            for c in string[1:]:
                if c < '0' or c > '9':
                    return False
        else:
            for c in string:
                if c < '0' or c > '9':
                    return False
    else:
        return False
    return True

def test_isint():
    assert(isint("") == False)
    assert(isint("a") == False)
    assert(isint("1a") == False)
    assert(isint("a1") == False)
    assert(isint("1+1") == False)
    assert(isint("1+") == False)
    assert(isint("1") == True)
    assert(isint("+1") == True)
    assert(isint("-1") == True)
    assert(isint("11212") == True)
    assert(isint("-11212") == True)
    assert(isint("+11212") == True)
    print("test_isint() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isintb(string, basis):
    """prüft, ob der String eine ganze Zahl zur Basis basis ist (Vorzeichen
    optional, nur Ziffern).
    Baue String mit gültigen Zeichen zusammen und prüfe dann, ob jedes
    Zeichen in diesem String vorkommt."""
    if basis > 1:
        gueltig = "" # "Liste" der gültigen Zeichen für geg. Basis
        if basis > 10: # Ziffern und Buchstaben (immer groß und klein)
            for z in range(10):
                gueltig += chr(z + ord('0'))
            for z in range(basis - 10):
                gueltig += chr(z + ord('a')) # Kleinbuchstabe
                gueltig += chr(z + ord('A')) # Großbuchstabe
        else: # nur Ziffern
            for z in range(basis):
                gueltig += chr(z + ord('0'))
        if len(string) > 0:
            if string[0] == '+' or string[0] == '-': # Vorzeichen
                for c in string[1:]:
                    if c not in gueltig:
                        return False
            else:
                for c in string:
                    if c not in gueltig:
                        return False
        else:
            return False
    else:
        return False
    return True # alles OK

def test_isintb():
    assert(isintb("", 2) == False)
    assert(isintb("a", 2) == False)
    assert(isintb("1a", 2) == False)
    assert(isintb("a1", 2) == False)
    assert(isintb("1+1", 2) == False)
    assert(isintb("1+", 2) == False)
    assert(isintb("1", 2) == True)
    assert(isintb("+1", 2) == True)
    assert(isintb("-1", 2) == True)
    assert(isintb("11212", 2) == False)
    assert(isintb("-11212", 2) == False)
    assert(isintb("+11212", 2) == False)
    assert(isintb("11212", 3) == True)
    assert(isintb("-11212", 3) == True)
    assert(isintb("+11212", 3) == True)
    assert(isintb("aBCdeF", 16) == True)
    assert(isintb("-aBCdeF", 16) == True)
    assert(isintb("+aBCdeF", 16) == True)
    print("test_isintb() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----

def sumdigits(zahl):
    """Berechnet die Ziffernsumme aus der gegebenen Zahl (int)"""
    sum = 0
    while zahl != 0:
        sum += zahl % 10 # Rest ist immer Einerstelle
        zahl = zahl // 10
    return sum

def test_sumdigits():
    assert(sumdigits(0) == 0)
    assert(sumdigits(1) == 1)
    assert(sumdigits(7) == 7)
    assert(sumdigits(10) == 1)
    assert(sumdigits(11) == 2)
    assert(sumdigits(123) == 6)
    assert(sumdigits(1010) == 2)
    print("test_sumdigits() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def wert_der_ziffer(ziffer):
    """liefert den Wert der Ziffer ziffer (0..9, bzw. A, B, C, ...)"""
    if ord(ziffer) <= ord("9"):
        wert = ord(ziffer) - ord("0")
    else:
        wert = ord(ziffer) - ord("A") + 10
    return wert

def horner(zahl, basis):
    """berechnet Dezimalzahl aus zahl (String) zur Basis basis.
    liefert Zahl. Hornerschema!"""
    dez = 0
    for ziffer in zahl:
        dez *= basis
        dez += wert_der_ziffer(ziffer)
    return dez

def test_horner():
    assert(horner("", 10) == 0)
    assert(horner("1", 10) == 1)
    assert(horner("12", 10) == 12)
    assert(horner("123", 10) == 123)
    assert(horner("1", 2) == 1)
    assert(horner("10", 2) == 2)
    assert(horner("11", 2) == 3)
    assert(horner("4", 16) == 4)
    assert(horner("A", 16) == 10)
    assert(horner("10", 16) == 16)
    print("test_horner() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isipv4(string):
    """Prüft, ob der gegebene String eine IPv4-Adresse enthält.
    IPv4-Adressen sind 4 Zahlen zwischen 0 und 255, die durch einen
    Punkt getrennt sind.
    Beispiele: 127.0.0.1, 193.170.149.129, 81.200.64.185
    Ungültige: 81.2010.64.185, 123.23.12, 127,0,0,1"""
    zahl = ""
    anzpunkte = 0
    anzziffer = 0
    for z in string:
        if '0' <= z <= '9': 
            zahl += z
            anzziffer += 1
        elif z == '.' and anzziffer > 0: # mindestens 1 Ziffer vor Zahl
            if not (0 <= horner(zahl, 10) <= 255):
                return False
            zahl = "" # neu beginnen
            anzziffer = 0
            anzpunkte += 1
        else:
            return False
    if anzpunkte == 3 and anzziffer > 0: # nach 3. Punkt eine Zahl!
        if not (0 <= horner(zahl, 10) <= 255):
            return False
        return True
    else:
        return False

def test_isipv4():
    assert(isipv4("127.0.0.1"))
    assert(isipv4("193.170.149.129"))
    assert(isipv4("81.200.64.185"))
    assert(not isipv4(""))
    assert(not isipv4("81.2010.64.185"))
    assert(not isipv4("123.23.12"))
    assert(not isipv4("123.23.12."))
    assert(not isipv4("123.23.12.3.3"))
    assert(not isipv4("123.23.12.3."))
    assert(not isipv4("127,0,0,1"))
    print("test_isipv4() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def anz_zahlen(string):
    """ermittelt die Anzahl der ganzen Zahlen im String"""
    anzahl = 0
    in_zahl = False
    for zeichen in string:
        if zeichen in "0123456789":
            if not in_zahl:
                anzahl += 1
                in_zahl = True
        else:
            in_zahl = False
    return anzahl

def test_anz_zahlen():
    string = "das ist eine Zahl 123 und noch eine 1 und noch eine 3213x23123"
    assert(4 == anz_zahlen(string))
    assert(0 == anz_zahlen("keine"))
    assert(1 == anz_zahlen("eine Zahl 123"))
    assert(2 == anz_zahlen("zwei 23 und 324"))
    assert(3 == anz_zahlen("drei 23 34 22"))
    assert(4 == anz_zahlen("vier 234 234 234 45"))
    assert(5 == anz_zahlen("5 234 234 234 45"))
    print("test_anz_zahlen() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sum_zahlen(string):
    """ermittelt die Summe der ganzen Zahlen im String. Eine Zahl
    ist eine ununterbrochene Folge von Ziffern.
    Beispiel: sum_zahlen("x 34 3 12k33 44 hallo 23") liefert 149"""
    summe = 0
    zahl = ""
    for zeichen in string:
        if zeichen in "0123456789":
            zahl += zeichen
        else:
            summe += horner(zahl, 10) # umrechnen und dazuzählen
            zahl = "" # neu beginnen
    if len(zahl) > 0: # zuletzt war Zahl, daher dazurechnen
        summe += horner(zahl, 10)
    return summe

def test_sum_zahlen():
    string = "das ist eine Zahl 123 und noch eine 1 und noch eine 3213x23123"
    assert(26460 == sum_zahlen(string))
    assert(0 == sum_zahlen("keine"))
    assert(123 == sum_zahlen("eine Zahl 123"))
    assert(347 == sum_zahlen("zwei 23 und 324"))
    assert(79 == sum_zahlen("drei 23 34 22"))
    assert(747 == sum_zahlen("vier 234 234 234 45"))
    assert(752 == sum_zahlen("5 234 234 234 45"))
    print("test_sum_zahlen() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def iskonto(string):
    """Prüft, ob der String eine gültige Kontonummer enthält. Dabei
    gelten folgende Regeln:
    1. genau 9 Ziffern (es dürfen Leerzeichen dazwischen sein
    2. die letzte Ziffer ist eine Prüfziffer und es gilt
       Die Kontonummer ist genau 9 Stellen lang und ist dann richtig, 
       wenn die folgendermaßen gebildete Summe auf Null endet:
       Ziffern mit ungeradem Index werden addiert(=Teilsumme1);
       Ziffern mit geradem Index werden verdoppelt und das jeweilige 
       Produkt addiert, wenn es einstellig ist, andernfalls wird die
       Quersumme des Produkts addiert(=Teilsumme2);
       Summe= teilsumme1 + Teilsumme2
    Beispiel:
       123456782 1+    3+    5+      7+     2  = 18 Teilsumme1
                   2*2+  4*2+  (1+2)+  (1+6)   = 22 Teilsumme2
                               6*2=12  8*2=16
                                               ------
                                                 40 Summe mod 10= Null
    Beispiele für Kontonummern:
       697199107 richtige Kontonummer
       723016689 richtige Kontonummer
       723016699 falsche Kontonummer
    """
    # entferne alle Leerzeichen (sind ja erlaubt)
    kontonummer = ""
    for c in string:
        if '0' <= c <= '9':
            kontonummer += c
        elif c != ' ':
            return False # nur Ziffern und Leerzeichen erlaubt!
    if len(kontonummer) != 9:
        return False
    
    summe = 0 # alles in einer Summe, aufteilen ist nicht nötig
    for i in range(len(kontonummer)):
        ziffer = ord(kontonummer[i]) - ord('0')
        if (1 + i) % 2 == 1: # ungerade
            summe += ziffer
        else: # gerade
            doppelt = ziffer * 2
            if doppelt >= 10: # Zwei Ziffern
                summe += doppelt % 10 # Einer
                summe += doppelt // 10 # Zehner
            else: # immer noch eine Ziffer
                summe += doppelt
    return summe % 10 == 0 # Summe muss durch 10 teilbar sein

def test_iskonto():
    assert(not iskonto(""))
    assert(not iskonto("a"))
    assert(not iskonto("69719a9107"))
    assert(not iskonto("69719917"))
    assert(not iskonto("723016699"))
    assert(iskonto("723016689"))
    assert(iskonto("697199107"))
    assert(iskonto("123456782"))
    assert(iskonto("12 345 6782"))
    print("test_iskonto() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def reverse_s(string):
    """liefert String in umgekehrter Reihenfolge zurück"""
    s = ""
    for c in string:
        s = c + s
    return s

def test_reverse_s():
    assert("" == reverse_s(""))
    assert("a" == reverse_s("a"))
    assert("ba" == reverse_s("ab"))
    assert("abc" == reverse_s("cba"))
    print("test_reverse() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def toupper(string):
    """liefert String in Großbuchstaben zurück"""
    s = ""
    for c in string:
        if 'a' <= c <= 'z':
            s += chr(ord(c) - ord('a') + ord('A'))
        elif c == 'ä':
            s += 'Ä'
        elif c == 'ö':
            s += 'Ö'
        elif c == 'ü':
            s += 'Ü'
        else:
            s += c
    return s

def test_toupper():
    assert("" == toupper(""))
    assert("A" == toupper("a"))
    assert("AB" == toupper("ab"))
    assert("ÄBC" == toupper("äbc"))
    assert("A" == toupper("a"))
    assert("AB" == toupper("Ab"))
    assert("ÄBC" == toupper("äBc"))
    print("test_toupper() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def tolower(string):
    """liefert String in Kleinbuchstaben zurück"""
    pass
    s = ""
    for c in string:
        if 'A' <= c <= 'Z':
            s += chr(ord(c) - ord('A') + ord('a'))
        elif c == 'Ä':
            s += 'ä'
        elif c == 'Ö':
            s += 'ö'
        elif c == 'Ü':
            s += 'ü'
        else:
            s += c
    return s

def test_tolower():
    assert("" == tolower(""))
    assert("a" == tolower("A"))
    assert("ab" == tolower("AB"))
    assert("äbc" == tolower("ÄBC"))
    assert("a" == tolower("A"))
    assert("ab" == tolower("Ab"))
    assert("äbc" == tolower("äBc"))
    print("test_tolower() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isupper(string):
    """prüft, ob String nur Großbuchstaben enthält"""
    for c in string:
        if 'a' <= c <= 'z' or c in "äöü": # ein Kleinbuchstabe
            return False
    return True

def test_isupper():
    assert(isupper(""))
    assert(isupper("A"))
    assert(isupper("AEÄÖ"))
    assert(not isupper("a"))
    assert(not isupper("abcö"))
    assert(not isupper("AbC"))
    print("test_isupper() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def islower(string):
    """prüft, ob String nur Kleinbuchstaben enthält"""
    for c in string:
        if 'A' <= c <= 'Z' or c in "ÄÖÜ": # ein Großbuchstab
            return False
    return True

def test_islower():
    assert(islower(""))
    assert(islower("a"))
    assert(islower("aeöä"))
    assert(not islower("A"))
    assert(not islower("ABCÖ"))
    assert(not islower("AbC"))
    print("test_islower() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def ispalindrom(string):
    """Prüft, ob der String ein Palindrom ist (von vorne und hinten
    gelesen gleich)."""
    for i in range(len(string)):
        if string[i] != string[-1 - i]:
            return False
    return True

def test_ispalindrom():
    assert(ispalindrom(""))
    assert(ispalindrom("a"))
    assert(ispalindrom("aa"))
    assert(ispalindrom("aba"))
    assert(ispalindrom("abba"))
    assert(not ispalindrom("ab"))
    assert(not ispalindrom("abb"))
    assert(not ispalindrom("abab"))
    print("test_ispalindrom() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def ispalindrom_l(liste):
    """Prüft, ob die Liste von vorne und hinten gelesen gleich ist."""
    return ispalindrom(liste) # sollte äquivalent sein!

def test_ispalindrom_l():
    assert(ispalindrom_l([]))
    assert(ispalindrom_l([1]))
    assert(ispalindrom_l([1, 1]))
    assert(ispalindrom_l([1, 2, 1]))
    assert(ispalindrom_l([1, 2, 2, 1]))
    assert(not ispalindrom_l([1, 2]))
    assert(not ispalindrom_l([1, 2, 2]))
    assert(not ispalindrom_l([1, 2, 1, 2]))
    print("test_ispalindrom_l() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def reverse_l(liste):
    """liefert die Liste in umgekehrter Reihenfolge zurück"""
    l = []
    for e in liste:
        l = [e] + l
    return l

def test_reverse_l():
    assert([] == reverse_l([]))
    assert([1] == reverse_l([1]))
    assert([1, 2] == reverse_l([2, 1]))
    assert([1, 2, 3] == reverse_l([3, 2, 1]))
    print("test_reverse_l() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sum(liste):
    """liefert die Summe aller Zahlen in der Liste"""
    summe = 0
    for e in liste:
        summe += e
    return summe

def test_sum():
    assert(0 == sum([]))
    assert(12 == sum([12]))
    assert(12 == sum([4, 8]))
    assert(12 == sum([4, 4, 4]))
    print("test_sum() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def mittelwert(liste):
    """liefert den Mittelwert (Durchschnitt) aller Zahlen in der Liste"""
    if len(liste) > 0:
        return sum(liste) / len(liste)
    else:
        return 0

def test_mittelwert():
    assert(0 == mittelwert([]))
    assert(1 == mittelwert([1]))
    assert(1.5 == mittelwert([1, 2]))
    assert(2 == mittelwert([2, 2, 2]))
    assert(2 == mittelwert([1, 2, 3]))    
    print("test_mittelwert() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def rmdups(liste):
    """(ReMove DUPlicates) liefert Liste, aus der alle Vielfachen entfernt
    (hintereinander).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 2, 3, 2, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 99, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    if len(liste) > 0:
        ret = [liste[0]]
        for e in liste:
            if ret[-1] != e: # letztes Element ungleich?
                ret.append(e)
        return ret
    else:
        return liste

def test_rmdups():
    assert([1, 2, 5, 2, 3, 2, 23] == rmdups([1, 2, 2, 2, 5, 2, 3, 2, 2, 23]))
    assert([99, 23, 99, 12, 2] == rmdups([99, 99, 23, 99, 99, 12, 2]))
    assert([] == rmdups([]))
    assert([1, 2, 4, 10] == rmdups([1, 2, 4, 10]))
    print("test_rmdups() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isin(item, liste):
    """ist item in liste enthalten?"""
    for e in liste:
        if item == e:
            return True
    return False

def rmalldups(liste):
    """(ReMove ALL DUPlicates) liefert Liste, aus der alle Vielfachen
    entfernt (müssen nicht hintereinander stehen).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 3, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    if len(liste) > 0:
        ret = [liste[0]]
        for e in liste:
#            if e not in ret: # schon vorhanden?
            if not isin(e, ret):
                ret.append(e)
        return ret
    else:
        return liste

def test_rmalldups():
    assert([] == rmalldups([]))
    assert([1, 2, 5, 3, 23] == rmalldups([1, 2, 2, 2, 5, 2, 3, 2, 2, 23]))
    assert([99, 23, 12, 2] == rmalldups([99, 99, 23, 99, 99, 12, 2]))
    assert([1, 2, 4, 10] == rmalldups([1, 2, 4, 10]))
    print("test_rmalldups() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def binsearch(liste, such):
    """liefert den Index von such in der sortierten Liste liste oder -1,
    wenn such in liste nicht vorkommt. Es ist die binäre Suche anzuwenden!
    """
    first = 0
    last = len(liste) - 1
    mid = (last - first) // 2 + first
    #print(first, mid, last)
    while len(liste) > 0 and first <= last:
        if such < liste[mid]:
            last =  mid - 1
        elif such > liste[mid]:
            first = mid + 1
        else:
            return mid #index found
        mid = (last - first) // 2 + first
        #print(first, mid, last)
    return -1 # not found

def test_binsearch():
    """static tests"""
    assert(binsearch([], 1) == -1)
    assert(binsearch([1], 2) == -1)
    assert(binsearch([1], 0) == -1)
    assert(binsearch([1], 1) == 0)
    assert(binsearch([1,3], 0) == -1)
    assert(binsearch([1,3], 2) == -1)
    assert(binsearch([1,3], 1) == 0)
    assert(binsearch([1,3], 3) == 1)
    assert(binsearch([1,3], 4) == -1)
    assert(binsearch([1,3,5], 2) == -1)
    assert(binsearch([1,3,5], 4) == -1)
    assert(binsearch([1,3,5], 1) == 0)
    assert(binsearch([1,3,5], 3) == 1)
    assert(binsearch([1,3,5], 5) == 2)
    assert(binsearch([1,3,5], 6) == -1)
    assert(binsearch([1,3,5,7], 0) == -1)
    assert(binsearch([1,3,5,7], 1) == 0)
    assert(binsearch([1,3,5,7], 3) == 1)
    assert(binsearch([1,3,5,7], 5) == 2)
    assert(binsearch([1,3,5,7], 7) == 3)
    assert(binsearch([1,3,5,7], 9) == -1)
    assert(binsearch([1,3,5,7], 2) == -1)
    assert(binsearch([1,3,5,7], 4) == -1)
    assert(binsearch([1,3,5,7], 6) == -1)
    import random
    for i in range(25):
        liste = [random.randint(0, 100) for i in range(100)]
        liste.sort()
        num = random.randint(0, 100)
        if num in liste:
            if binsearch(liste, num) < 0:
                print(liste)
                print("%d should be found in list" % num)
                assert(binsearch(liste, num) >= 0)
        else:
            if binsearch(liste, num) >= 0:
                print(liste)
                print("%d should not be found in list" % num)
                assert(binsearch(liste, num) < 0)
    print("test_binsearch() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sumteilbar(liste, teiler):
    """Berechnet die Summe aller durch teiler teilbaren Zahlen der Liste"""
    summe = 0
    for e in liste:
        if e % teiler == 0:
            summe += e
    return summe

def test_sumteilbar():
    assert(0 == sumteilbar([], 2))
    assert(0 == sumteilbar([1], 2))
    assert(0 == sumteilbar([1, 3], 2))
    assert(0 == sumteilbar([1, 3, 23], 2))
    assert(2 == sumteilbar([2], 2))
    assert(2 == sumteilbar([1, 2], 2))
    assert(2 == sumteilbar([1, 2, 23], 2))
    assert(32 == sumteilbar([1, 8, 24], 2))
    assert(27 == sumteilbar([3, 8, 24], 3))
    print("test_sumteilbar() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def issorted(liste):
    """Prüft, ob die Liste sortiert ist, d.h. ob e1 <= e2 <= e3 <= ... <= en
    e1, e2, ... en sind die n Elemente der Liste mit Länge n"""
    for i in range(len(liste) - 1):
        if liste[i] > liste[i + 1]:
            return False
    return True

def test_issorted():
    assert(issorted([]))
    assert(issorted([1]))
    assert(issorted([1, 1]))
    assert(issorted([1, 3, 3, 4]))
    assert(not issorted([1, 0]))
    assert(not issorted([1, 2, 1]))
    print("test_issorted() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def printlxl(liste1, liste2):
    """gibt eine Multiplikationstabelle der beiden Listen aus.
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       printlxl(a, b) gibt dann folgendes aus:
         3   2   2 
         6   4   4 
        12   8   8 
        15  10  10 
    Die erste Liste bestimmt die Anzahl der Zeilen, die zweite die Anzahl 
    der Spalten. Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    erg = lxl(liste1, liste2)
    for zeile in erg:
        for spalte in zeile:
            print("%4d" % spalte, end = "")
        print()

def test_printlxl():
    a = [1, 2, 4, 5]
    b = [3, 2, 2]
    printlxl(a, b)
    print("test_printlxl() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def lxl(liste1, liste2):
    """liefert eine Multiplikationstabelle der beiden Listen (Liste von 
    Listen).
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       lxl(a, b) liefert folgende Liste zurück:
       [[3, 2, 2], [6, 4, 4], [12, 8, 8], [15, 10, 10]]
       übersichtlich formatiert:
       [[3, 2, 2], 
        [6, 4, 4], 
        [12, 8, 8], 
        [15, 10, 10]]
    Die erste Liste bestimmt die Anzahl der "Zeilen", die zweite die Anzahl 
    der "Spalten". Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    erg = []
    if len(liste2) > 0:
        for a in liste1:
            zeile = []
            for b in liste2:
                zeile.append(a * b)
            erg.append(zeile)
    return erg

def test_1x1():
    a = [1, 2, 4, 5]
    b = [3, 2, 2]
    assert([[3, 2, 2], [6, 4, 4], [12, 8, 8], [15, 10, 10]] == lxl(a, b))
    assert([] == lxl([], []))
    assert([] == lxl([], [1, 2]))
    assert([] == lxl([1, 2], []))
    print("test_1x1() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
# Testaufrufe:
test_isint()
test_isintb()
test_sumdigits()
test_horner()
test_isipv4()
test_anz_zahlen()
test_sum_zahlen()
test_iskonto()
test_reverse_s()
test_toupper()
test_tolower()
test_isupper()
test_islower()
test_ispalindrom()
test_ispalindrom_l()
test_reverse_l()
test_sum()
test_mittelwert()
test_rmdups()
test_rmalldups()
test_binsearch()
test_sumteilbar()
test_issorted()
test_printlxl()
test_1x1()

Die Funktionen isupper() und islower() sind eigentlich nicht korrekt, denn liefert z.B. isupper("A4C") True. Man müsste also eher schreiben:

def isupper(string):
    """prüft, ob String nur Großbuchstaben enthält"""
    for c in string:
        if not ('A' <= c <= 'Z' or c in "ÄÖÜ"):
            return False
    return True

Labels: , ,


Samstag, 22. Januar 2011

 

Ermittle die Top-10 der Worte in einem Text (POS1: 2A, 2C)

Aufgabe 1

Schreiben Sie eine Funktion hauf(string), welche die 10 häufigsten Worte im String string ermittelt und als Liste von Tupeln der Art (wort, anzahl) zurück liefert (Länge der Liste ist <= 10). Die Liste muss so sortiert sein, dass das Häufigste Wort ganz am Anfang ist. Haben Worte die selben Häufigkeiten, so haben diese alphabetisch sortiert zu sein.




Aufgabe 2

Schreiben Sie eine Funktion histogramm(liste, weite = 60), welche aus der übergebenen Liste ein Histogramm erzeugt und als String zurück liefert. Eine „Zeile“ soll so aussehen:
das            123 ************
Wobei für das Wort 14 Zeichen, die Anzahl 3 Zeichen vorgesehen werden müssen. Dahinter ist eine Anzahl Sternchen, die der Anzahl bezogen auf das Maximum (höchste Anzahl in der Liste) und der Weite entspricht (round(anzahl / max * weite)).


Aufgabe 3

Schreiben Sie eine Funktion main(), welche alle auf der Kommandozeile angegebenen Dateien öffnet und die Gesamthäufigkeiten ermittelt (also hauf() mit dem gesamten gelesenen Text aufruft) und mit den ermittelten Daten das Histogramm ausgibt.
Sind keine Dateien angegeben, so ist von stdin zu lesen (bis EOF).
Beispielaufruf mit den Testdaten vom /vorgabe-Verzeichnis:
hp@l111~/workspace/python$ python3 haufdiagramm.py testdaten.txt
der            89 ************************************************************
die            61 *****************************************
und            43 *****************************
Histogramm     40 ***************************
in             28 *******************
für            17 ***********
ist            17 ***********
den            16 ***********
das            15 **********
ein            14 *********

Mögliche Lösung

Ich werde die einzelnen Funktionen erklären:
import sys
Dieser Import wird für die Kommandozeilenargumente benötigt.

Folgende Funktion ermittelt die Wort-Häufigkeiten, sortiert das Ergebnis nach Häufigkeit und liefert die ersten 10 Elemente.
def hauf(string):
    """ermittelt die 10 häufigsten Worte in 'string' und liefert Liste mit 
    Tupel (wort, anzahl)"""
    dict = {}
    for wort in string.split():
split() ohne Parameter zerlegt einen String in eine Liste von "Worten", wobei ein "Wort" eine Folge von Zeichen ohne Leerzeichen, Tabulator und Zeilenumbruch ist. Mit anderen Worten Leerzeichen, Tabulatoren und Zeilenumbrüche sind die Trenner.
        wort = wort.rstrip('.,;?!")') # Satzzeichen entfernen
        wort = wort.lstrip('"(') # Hochkomma und Klammer dürfen vorne stehen
lstrip() und rstrip() entfernen alle Zeichen in Klammer llinks bzw. rechts. In unserem Fall alle Satzzeichen, die unmittelbar vor einem Wort bzw. nach einem Wort stehen dürfen.
Die beiden Zeilen könnte man natürlich auch zusammenfassen zu: wort = wort.rstrip('.,;?!")').lstrip('"(').
Die folgende erste Zeile prüft, ob der String auch tatsächlich ein Wort ist (nur Buchstaben).
        if wort.isalpha():
            if wort in dict:
                dict[wort] += 1
            else:
                dict[wort] = 1
Obige 4 letzte Zeilen prüfen zunächst, ob das Wort bereits im Dictionary enthalten ist. Wenn ja, dann wird die gespeicherte Anzahl erhöht, wenn nein, dann wird unter diesem Wort eine 1 gespeichert.
        l = list(dict.items())
Erzeugt eine Liste mit Tupeln (wort, anzahl) aus dem Dictionary.
        l.sort(key=lambda x: (-x[1], x[0]))
sort() sortiert eine Liste nach der natürlichen Reihenfolge. Mit dem Parameter key kann eine Funktion übergeben werden, anhand deren Returnwert sortiert werden soll. Die Funktion wird jeweils mit einem Listenelement aufgerufen. Man könnte also eine Funktion schreiben, die jeweils das zweite Element des Tupels (die Liste besteht in diesem Fall ja aus Tupeln der Art (wort, anzahl)) zurückliefert. Da absteigend sortiert werden soll, muss entweder der Wert mit -1 multipliziert werden (-[x1]) oder man muss beim Sortieren sort(reverse=True) aufrufen. Den Namen dieser Funktion übergibt man dann per key=sortfunktion.
Diese Funktion könnte so aussehen: def sortierfunktion(x):return (-x[1], x[0])
lambda x: definiert eine anonyme Funktion (Funktion ohne Namen) mit dem Parameter x, die gleich an Ort und Stelle definiert und übergeben werden kann.
    l = l[0:10]
    return l
Die letzten beiden Zeilen schneiden die ersten 10 Elemente der Liste aus und liefern sie zurück.
Das könnte man auch nur mit einer Anweisung machen: return l[:10].

def stars(num, max, width):
    """liefert String mit so vielen Sternen, wie num / max * width"""
    return "*" * round(num / max * width)
Dazu braucht man nicht allzu viel sagen.

Die folgende Funktion erzeugt das in Aufgabe 2 geforderte Histogramm.

Um einen passenden Maßstab für die geforderte Weite weite=60 zu finden, wird das Maximum der Werte ermittelt. Das muss natürlich das zweite Element des ersten Tupels der Liste sein, wenn überhaupt Tupel in der Liste sind.
def histogramm(liste, weite=60):
    """erzeugt Histogramm und liefert es zurück"""
    if len(liste) > 0:
        max = liste[0][1]
    ret = ""
    for wort, anzahl in liste:
Diese Schleife geht jedes Tupel der Liste durch, wobei die Tupel gleich in wort und anzahl "ausgepackt" wird.
        ret += "%-14s%3d %s\n" % (wort, anzahl, stars(anzahl, max, weite))
Hier wird jede Zeile an den String ret angehängt, wobei Stringformatierung angewendet wird.
    return ret

Die folgenden beiden Funktionen dient nur zum Testen mit fixen Strings und liefert einen String mit vorgegebenen Anzahlen von Worten.
def teststr():
    """liefert Testeingabe"""
    s = "Häufigkeit " * 50 + "das " * 123 + "die, " * 100 + "Haus " * 40
    s += "Maus " * 38 + "Koffer " * 34 + "Kilogramm " * 22 + "Punkt " * 18
    s += "Genau " * 16 + "Zehn " * 10
    s += "nichts " * 5 + "23 " * 50 + "ziemlich " * 3
    return s 

def test():
    """Testen..."""
    liste = hauf(teststr())
    print(histogramm(liste, 12.3))

Diese Funktion prüft, ob überhaupt Kommandozeilenargumente vorhanden sind. Gibt es welche, so werden alle Argumente als Dateien zum Lesen geöffnet und mit read() komplett gelesen und an den String eingabe angehängt.
Gibt es keine Dateinamen auf der Kommandozeile, so wird mittels eingabe = sys.stdin.read() von der Standardeingabe gelesen.
Die letzte Zeile ruft die beiden Hauptfunktionen auf und gibt das Ergebnis aus.
Es fehlt allerdings noch die Überprüfung, ob eine Datei überhaupt existiert bzw. geöffnet werden kann. Diesen Teil möge der Leser selber ergänzen.
def main():
    """öffnet Datei oder liest von stdin"""
    if len(sys.argv) > 1:
        eingabe = ""
        for arg in sys.argv[1:]:
            f = open(arg)
            eingabe += " " + f.read()
            f.close()
    else:
        eingabe = sys.stdin.read()
    print(histogramm(hauf(eingabe)))
    

Die folgenden Zeilen dienen zum Aufrufen der Funktion main() (bzw. test() zum Testen), sofern dieses Script als Programm aufgerufen wurde.
if __name__ == '__main__':
#    test()
    main()

Vollständiges Programm (ohne Header)

import sys

def hauf(string):
    """ermittelt die 10 häufigsten Worte in 'string' und liefert Liste mit 
    Tupel (wort, anzahl)"""
    dict = {}
    for wort in string.split():
        wort = wort.rstrip('.,;?!")') # Satzzeichen entfernen
        wort = wort.lstrip('"(') # Hochkomma und Klammer dürfen vorne stehen
        if wort.isalpha():
            if wort in dict:
                dict[wort] += 1
            else:
                dict[wort] = 1
    l = list(dict.items())
    l.sort(key=lambda x: (-x[1], x[0]))
    l = l[0:10]
    return l

def stars(num, max, width):
    """liefert String mit so vielen Sternen, wie num / max * width"""
    return "*" * round(num / max * width)

def histogramm(liste, weite=60):
    """erzeugt Histogramm und liefert es zurück"""
    if len(liste) > 0:
        max = liste[0][1]
    ret = ""
    for wort, anzahl in liste:
        ret += "%-14s%3d %s\n" % (wort, anzahl, stars(anzahl, max, weite))
    return ret

def teststr():
    """liefert Testeingabe"""
    s = "Häufigkeit " * 50 + "das " * 123 + "die, " * 100 + "Haus " * 40
    s += "Maus " * 38 + "Koffer " * 34 + "Kilogramm " * 22 + "Punkt " * 18
    s += "Genau " * 16 + "Zehn " * 10
    s += "nichts " * 5 + "23 " * 50 + "ziemlich " * 3
    return s 

def test():
    """Testen..."""
    liste = hauf(teststr())
    print(histogramm(liste, 12.3))

def main():
    """öffnet Datei oder liest von stdin"""
    if len(sys.argv) > 1:
        eingabe = ""
        for arg in sys.argv[1:]:
            f = open(arg)
            eingabe += " " + f.read()
            f.close()
    else:
        eingabe = sys.stdin.read()
    print(histogramm(hauf(eingabe)))
    
if __name__ == '__main__':
#    test()
    main()

Labels: , , ,


Freitag, 14. Januar 2011

 

Ganze Zahlen zählen (Schleife, Sequenzen, Funktionen POS1: 1BHIF)

Schreiben Sie ein Programm anzzahlen.py, welches eine Zeile einliest und in dieser Zeile zähle, wieviele ganze Zahlen vorkommen. Jedes Zeichen, das keine Ziffer ist, gilt als nicht zu einer Zahl gehörend.
Beispiel:
Eingabe:
das ist eine Zahl 123 und noch eine 1 und noch zwei 3213x23123
Ausgabe:
4 Zahlen gefunden.
Das Programm soll richtige Sätze ausgeben, also "Keine Zahlen gefunden.", wenn in der Eingabe keine Zahlen sind oder "Eine Zahl gefunden.", wenn eine Zahl in der Eingabe war. Ab 4 soll dann die Zahl ausgegeben werden, z.B. "6 Zahlen gefunden."

Mögliche Lösung:
"""
author: Haberstroh, Harald
matnr:  d09666
file:   anzzahlen.py
desc:   Zähle ganze Zahlen
        Beispiel:
            das ist eine Zahl 123 und noch eine 1 und noch zwei 3213x23123
        Ausgabe:
            4 Zahlen gefunden
date:   14.1.2011
class:  Lehrer
catnr:  33
"""
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----

def anz_zahlen(string):
    """ermittelt die Anzahl der ganzen Zahlen im String"""
    anzahl = 0
    in_zahl = False
    for zeichen in string:
        if zeichen in "0123456789":
            if not in_zahl:
                anzahl += 1
                in_zahl = True
        else:
            in_zahl = False
    return anzahl

def ausgabe(anzahl):
    """gibt deutschen Satz aus mit Anzahl"""
    if anzahl == 0:
        print("Keine Zahl gefunden.")
    elif anzahl == 1:
        print("Eine Zahl gefunden.")
    elif anzahl == 2:
        print("Zwei Zahlen gefunden.")
    elif anzahl == 3:
        print("Drei Zahlen gefunden.")
    else:
        print(anzahl, "Zahlen gefunden.")
        
def main():
    """EVA Eingave Verarbeitung Ausgabe - das Hauptprogramm"""
    string = input("> ")
    anzahl = anz_zahlen(string)
    ausgabe(anzahl)

def test():
    """Testen von anz_zahlen() und ausgabe()"""
    string = "das ist eine Zahl 123 und noch eine 1 und noch eine 3213x23123"
    anzahl = anz_zahlen(string)
    ausgabe(anzahl)
    ausgabe(anz_zahlen("keine"))
    ausgabe(anz_zahlen("eine Zahl 123"))
    ausgabe(anz_zahlen("zwei 23 und 324"))
    ausgabe(anz_zahlen("drei 23 34 22"))
    ausgabe(anz_zahlen("vier 234 234 234 45"))
    ausgabe(anz_zahlen("5 234 234 234 45"))

    
if __name__ == '__main__':
    main()
#    test()
Beachten Sie, dass ich hier eine eigene Funktion test() gemacht habe, um viele Testfälle immer wieder leicht probieren zu können. Statt main() muss dann nur test() aufgerufen werden.
Das Programm wäre auch ohne die Funktion test() fertig und korrekt. test() half nur bei der Entwicklung bei der Fehlersuche.

Labels: , , ,


Freitag, 3. Dezember 2010

 

Musterlösung zu Übungsbeispiele für Python vom 1.12.2010 (POS1: 1BHIF)

Prüfen, ob durch die Angabe dreier Längen ein Dreieck gegeben ist.
Zwei Seiten müssen immer länger als die dritte sein. Man muss dies für alle Möglichkeiten prüfen, denn z.B.
a = 3, b = 4, c = 1
Da stimmt zwar a + b > c (7 > 1) und b + c > a (4 > 3), jedoch nicht a + c > b (4 == 4)!
Bei der Lösung wird eine Schleife verwendet.
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    i = 3
    while i > 0:
        if a + b <= c:
            return False # kein Dreieck
        a, b, c = b, c, a
        i = i - 1
    return True
Man könnte das Problem natürlich auch durch logische Verknüpfung der Prüfungen lösen:
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    if a + b > c and b + c > a and c + a > b:
        ret = True
    else:
        ret = False
    return ret
Oder gleich so:
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    return a + b > c and b + c > a and c + a > b
Bei der zweiten Aufgabe soll geprüft werden, ob durch zwei Seiten und einem Winkel ein Quadrat gegeben ist. Die beiden Seiten müssen gleich lang sein (und größer 0) und der Winkel 90°:
def ist_quadrat(a, b, w):
    """liefert True, wenn durch a und b sowie dem Winkel w ein 
    Quadrat definiert ist"""
    quadrat = False
    if a > 0:
        if a == b:
            if w == 90:
                quadrat = True
    # else entfällt, da die Variable quadrat schon mit False vorbelegt ist.
    return quadrat
Oder wieder kürzer mit logischer Verknüpfung:
def ist_quadrat(a, b, w):
    """liefert True, wenn durch a und b sowie dem Winkel w ein 
    Quadrat definiert ist"""
    return a == b and w == 90 and a > 0
Die Schaltjahrprüfung könnte so erfolgen:
def ist_schaltjahr(jahr):
    """liefert True, wenn das gegebene Jahr ein Schaltjahr ist.
    Seit Ende 1582 gilt der Gregorianische Kalender, bei dem folgende Regel
    für die Schaltjahrbestimmung anzuwenden sind:
        In allen Jahren, deren Jahreszahl durch vier teilbar ist, ist der 
        29. Februar ein Schalttag und damit ist dieses Jahr ein Schaltjahr.
        Eine Ausnahme bilden allerdings die vollen Jahrhundertjahre 1700, 
        1800, 1900 usw., auch Säkularjahre genannt. Hiervon erhalten nur 
        diejenigen einen Schalttag, deren Jahreszahl durch 400 teilbar ist. 
        Jedes vierte Säkularjahr ist somit ein Schaltjahr.
    Für alle Jahre <= 1582 liefert die Funktion False, weil da das
    Schaltjahr nicht definiert ist, sonst gilt obige Regel."""
    if jahr <= 1582:
        schaltjahr = False
    elif jahr % 400 == 0:
        schaltjahr = True
    elif jahr % 100 == 0:
        schaltjahr = False
    elif jahr % 4 == 0:
        schaltjahr = True
    else:
        schaltjahr = False
    return schaltjahr
Jahre bis 1582 können keine Schaltjahre sein, da der Gregorianische Kalender erst im Jahr 1582 definiert wurde. Zum Test der Schaltjahrprüfung noch ein paar Worte, aber zunächst die Testfunktion, die ein paar Jahreszahlen prüft, von denen man weiß, dass sie kein Schaltjahr sind sowie Zahlen, bei denen bekannt ist, dass sie Schaltjahre sind:
def test_ist_schaltjahr():
    for jahr in (1000, 1580, 1581, 1582, 1900, 1999, 2003):
        assert not ist_schaltjahr(jahr)
    for jahr in (1996, 2000, 2004):
        assert ist_schaltjahr(jahr)
    print("ist_schaltjahr ok")
Die Anweisung assert ist für Programmierer und dient zum Prüfen der Bedingung, die gleich dahinter folgt, gedacht. Zum Beispiel:
assert 3 < 4
Die Bedingung ist wahr, daher läuft das Programm weiter. Ist die Bedingung hingegen falsch, so wird das Programm mit einer Fehlermeldung abgebrochen:
>>> assert 3 > 4
Traceback (most recent call last):
  File "", line 1, in 
    assert 3 > 4
AssertionError
Zusätzlich kann ein Text als Fehlermeldung angegeben werden:
>>> assert 3 > 4, "3 ist nicht größer als 4"
Traceback (most recent call last):
  File "", line 1, in 
    assert 3 > 4, "3 ist nicht größer als 4"
AssertionError: 3 ist nicht größer als 4
Angewendet auf obige Testfunktion bedeutet das, dass immer, wenn assert not ist_schaltjahr(jahr) die Funktion ist_schaltjahr(jahr) den Wert False leifern muss und wenn assert ist_schaltjahr(jahr) steht, True geleifert werden muss, damit das Programm nicht abgebrochen wird. Der nächste Punkt ist die for jahr in (1000, 1580, 1581, 1582, 1900, 1999, 2003):-Zeile. Die Variable jahr bekommt der Reihe nach die Werte 1000, 1580, 1581, 1582, 1900, 1999, 2003 und der eingerückte Code-Block (die eine Zeile) wird ausgeführt. Das ist die for-Schleife, welche im Buch im Kapitel 7 auch erklärt wird. Die Funktion test_ist_schaltjahr() prüft also zunächst ein paar Jahre, die kein Schaltjahr sind und ein paar Schaltjahre. Die Fibonaccizahlen können wie folgt ermittelt werden:
def fib(n):
    """liefert die Fibonaccizahl zu n. Die Zahlen sind wie definiert:
        fib(0) -> 0
        fib(1) -> 1
        fib(2) -> 1
        fib(3) -> 2
        fib(4) -> 3
        fib(5) -> 5
        fib(6) -> 8
        ...
        fib(10) -> 55
        ...
        fib(15) -> 610"""
    f = 0
    a = 0
    b = 1
    i = 0
    while i < n:
        f = a + b
        b, a = a, f
        i += 1
    return f
Für n = 0 und n = 1 sind die (Anfangs-)Werte vordefiniert. fib(0) = 0 und fib(1) = 1. Danach gilt die Regel, dass die n. Fibonaccizahl die Summe der beiden Vorgänger ist, also fib(n - 1) + fib(n - 2) (Siehe dazu auch Wikipedia: Fibonacci-Folge). Die Variable f hat immer den Wert der aktuellen Fibonaccizahl und ist daher mit 0 initialisiert. Die Variable a und b haben immer den Wert der beiden Vorgänger. Sie können das leicht sehen, wenn Sie print(...)-Anweisungen einfügen und sich dadurch den Ablauf der Berechnung anzeigen lassen:
def fib(n):
    """liefert die Fibonaccizahl zu n."""
    f = 0
    a = 0
    b = 1
    i = 0
    while i < n:
        f = a + b
        print(i, ":  f =", f, "a =", a, "b =", b) # Testausgabe
        b, a = a, f
        i += 1
    return f

print(fib(10))
..gibt dann folgendes aus:
0 :  f = 1 a = 0 b = 1
1 :  f = 1 a = 1 b = 0
2 :  f = 2 a = 1 b = 1
3 :  f = 3 a = 2 b = 1
4 :  f = 5 a = 3 b = 2
5 :  f = 8 a = 5 b = 3
6 :  f = 13 a = 8 b = 5
7 :  f = 21 a = 13 b = 8
8 :  f = 34 a = 21 b = 13
9 :  f = 55 a = 34 b = 21
55
Die Ausgabe einer Tabelle mithilfe der Funktion fib() ist einfach (oder doch etwas kompliziert, weil die eckigen Klammern dazukommen):
def fibtab(n):
    """erzeugt eine Tabelle der Fibonaccizahlen bis inclusive n.
    z.B. gibt fibtab(10) folgendes aus:
        [0, 1, 1, 2, 3, 5, 8]
    oder fibtab(100):
        [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    Hinweis:
        verwenden Sie als Zeilenumbruch ", " beim print, also
            print(wert, end=', ')
        außer natürlich beim letzten Wert, hier ist "]" gefordert.
    """
    i = 0
    print("[", end = "")
    f = fib(i)
    while f <= n:
        print(f, end = "")
        i += 1
        f = fib(i)
        if f > n:
            print("]")
        else:
            print(", ", end = "")
Wir müssen sicherstellen, dass zunächst "[" ohne Zeilenumbruch ausgegeben wird. Die Funktion print(...) hat einen optionalen Parameter end, der angibt, was zum Schluss ausgegeben werden soll. Standardmäßig ist das "\n", was einen Zeilenumbruch bewirkt. Setzt man den Wert end = "", so wird ein Leerstring, also nichts, ausgegeben. In der Schleife muss man noch prüfen, ob man das Ende erreicht hat und in diesem Fall "]" (mit Zeilenumbruch) ausgibt. Im Normalfall wird ein Komma ohne Zeilenumbruch ausgegeben (letzte Zeile). Beachten Sie, dass der Parameter n hier die Grenze der Fibonaccizahl angibt und nicht die wievielte (wie bei fib(n)). Die Lösung zum Beispiel, die Summe der Vielfachen von 3 und 5 kleiner als ein Maximum:
def sum_3_5(max):
    """
    bilde Summe der Vielfachen von 3 und 5 kleiner als max.
    Beispielsweise liefert sum_3_5(10) 23, weil
        die Vielfachen von 3 < 10 sind
            3 + 6 + 9 = 18
        die Vielfachen von 5 < 10 sind
            5
        18 + 5 = 23
    sum_3_5(20) liefert 78, weil
        3+  6+9+   12+15+18=63
          5+    10+        =15 (15 ist in obiger Liste schon enthalten!)
    sum_3_5(25) liefert 143, weil
        3+  6+9+   12+   18+   21+24=93 (Vielfache von 5 stehen darunter)
          5+    10+   15+   20      =50
    sum_3_5(100) liefert 2318 (ohne Beweis)
    """
    summe = 0
    i = 0
    while i < max:
        if i % 5 == 0:
            summe += i
        elif i % 3 == 0:
            summe += i
        i += 1
    return summe
Die Laufvariable i wird immer dann zur summe dazugezählt, wenn sie durch 5 teilbar ist oder wenn sie durch 3 teilbar ist. Der Operator % berechnet den Rest der Division. Ist der Rest 0, so ist die erste durch die zweite Zahl teilbar. Das Beispiel zum finden des nächstkleineren bzw. gleichen Quadrats kann so mit einer Probiermethode gelöst werden:
def minquad(zahl):
    """finde die nächstkleinere oder gleichgroße ganze Zahl, die eine 
    Quadratzahl ist. Beispiele:
        minquad(16) -> 16 (weil 4 * 4 = 16 <= 16)
        minquad(24) -> 24 (weil 4 * 4 = 16 <= 14)
        minquad(25) -> 25 (weil 5 * 5 = 25 <= 25)
        minquad(26) -> 25 (weil 5 * 5 = 25 <= 26)
    """
    i = 0
    mq = 0
    while i ** 2 <= zahl:
        mq = i ** 2
        i += 1
    return mq
i ** 2 bedeutet i2. Man könnte natürlich auch i * i schreiben. Diese Funktion prüft einfach sukzessive alle Quadrate, bis eines gefunden wurde, das größer als die gegebene Zahl ist. Der Wert davor (die Variable mq) ist natürlich das Ergebnis, weil das der größte Wert war, für den die Bedingung gilt. Es geht natürlich schneller und einfacher, wenn man sich eine Formel überlegt:
from math import sqrt
from math import floor
def minquad(zahl):
    """finde die nächstkleinere oder gleichgroße ganze Zahl, die eine 
    Quadratzahl ist. Beispiele:
        minquad(16) -> 16 (weil 4 * 4 = 16 <= 16)
        minquad(24) -> 16 (weil 4 * 4 = 16 <= 24)
        minquad(25) -> 25 (weil 5 * 5 = 25 <= 25)
        minquad(26) -> 25 (weil 5 * 5 = 25 <= 26)
    """
    return int(floor(sqrt(zahl))) ** 2
Die Funktion floor(x) liefert die nächstkleinere ganze Zahl zu x (bei positiven Zahlen schneidet dir Funktion die Nachkommastellen der übergebenen Zahl ab). Z.B:
>>> from math import floor
>>> floor(3.14)
3
>>> floor(3)
3
>>> floor(-3.14)
-4
Wenn man die nächstkleinere ganze Zahl der Wurzel quadriert, hat man das gewünschte Ergebnis. Die Turtle-Funktion zum Dreieckzeichnen könnte wie folgt aussehen und braucht nicht weiter erklärt werden:
def dreieck(a, w):
    """zeichnet ein gleichseitiges Dreieck der Seitenlänge a, gedreht um
    den Winkel w. w = 0 bedeutet, dass die eine Seite horizional ist. w = 90
    bedeutet, dass das Dreieck nach LINKS um 90° gedreht ist, d.h. eine Seite
    ist vertikal.
    Das Dreieck soll rechts neben des angegebenen Punktes gezeichnet werden.
    """
    # nehme an Turtle sieht nach oben (wie auch beim Starten)
    right(90 - w)
    pendown()
    for i in range(3):
        forward(a)
        left(120)
    penup()
    left(90 - w)
Um ein Dreieck an eine bestimmte Position zu setzen, kann zusätzlich die Funktion setpos(x, y), welche die Turtle an die Postion x,y setzt, verwendet werden:
def dreieck_an_pos(a, w, x, y):
    """zeichnet ein Dreieck mit Hilfe der Funktion dreieck(a, w) an der 
    Position x, y."""
    penup()
    setpos(x, y)
    dreieck(a, w)
Abgesehen von den Turtle-Funktionen können Sie die Funktionen direkt auf der Website http://codepad.org testen. Dort können Sie auch andere Programmiersprachen probieren. Die Python-Version ist leider nicht aktuell, daher könnte es sein, dass einiges doch nicht direkt so funktioniert. Diese einfachen Beispiele funktionieren aber.
Hier ist zu minquad(zahl) zu sehen:

Labels: , ,


Donnerstag, 2. Dezember 2010

 

Datum in C selbst gebastelt

Datumsberechnungen sind immer wieder eine Quelle großer langwieriger, fehleranfälliger Programmteile. Vor allem in Sprachen, die solche Datumsoperationen nicht unterstützen. Sie sind auch immer wieder geeignet, Programmieren zu üben. Hier die Musterlösung zu einem einfachen Kalenderprogramm in C, bei dem ein Großteil der Datumsfunktionen selbstgestrickt sind. Viel Spaß damit!
Verwendung:
Ohne Parameter gibt es einfach das aktuelle Monat mit Markierung < 2> für den heutigen Tag und : 8: als Markierung für österreichische Feiertage. Danach wird die Liste der Feiertage ausgegeben.
$ ./cal 
Dezember 2010

    Montag       6  13  20  27     
  Dienstag       7  14  21  28     
  Mittwoch   1 : 8: 15  22  29     
Donnerstag < 2>  9  16  23  30     
   Freitag   3  10  17 :24::31:    
   Samstag   4  11  18 :25:        
   Sonntag : 5::12::19::26:    

 5.12. 2. Advent
 8.12. Maria Empfängnis
12.12. 3. Advent
19.12. 4. Advent
24.12. Heiligabend
25.12. Christtag
26.12. Stefanitag
31.12. Silvester :-)

Here comes the Source...

/*
 * File:    cal.c
 *
 * Zweck:   Kalenderprogramm
 *          ...für ein gegebenes Monat.
 *          mit/ohne argc/argv
 *
 * Autor:   Harald Haberstroh
 *
 * Algorithmus:
 *
 * History: 2002-09-29
 *          2003-10-15, Feiertage + Argumente von main()
 *          2004-11-05, Liste der Feiertage
 */

/*--------------------------- includes ------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

/*--------------------------- defines -------------------------------*/
#define  WOCHENTAG   3                 /* Wochentag des 1.1.1582 */
#define  MONTAG 0
#define  DIENSTAG 1
#define  MITTWOCH 2
#define  DONNERSTAG 3
#define  FREITAG 4
#define  SAMSTAG 5
#define  SONNTAG 6

/*--------------------------- typedefs ------------------------------*/

/*--------------------------- globals -------------------------------*/
enum {
   KEIN = 0,
   NEUJAHR,
   HL3KOENIGE,
   ASCHERMITTWOCH,
   PALMSONNTAG,
   KARFREITAG,
   OSTERSONNTAG,
   OSTERMONTAG,
   STAATSFEIERTAG,
   MUTTERTAG,
   PFINGSTSONNTAG,
   PFINGSTMONTAG,
   DREIFALTIGKEITSSONNTAG,
   FRONLEICHNAM,
   MARIAHIMMELFAHRT,
   NATIONALFEIERTAG,
   REFORMATIONSTAG,
   ALLERHEILIGEN,
   ALLERSEELEN,
   MARIAEMPFAENGNIS,
   ADVENT1,
   ADVENT2,
   ADVENT3,
   ADVENT4,
   HEILIGABEND,
   CHRISTIANI,
   STEFANI,
   SILVESTER
};

/*-------------------------- prototypes -----------------------------*/
void printFeiertage(int jahr, int monat);
int tagemonat( int jahr, int monat );
int sonntag( int jahr, int monat, int tag );
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag );
void ostern( int jahr, int *monat, int *tag );
int feiertag(int jahr, int monat, int tag);
int anzProMonat(int jahr, int monat);
void printcal(int start, int anz, int heute, int monat, int jahr);
int schaltjahr( int Jahr );
int Anzahl_Tage( int Jahr, int Monat, int Tag );

/*----------------------------- main --------------------------------*/
int main( int argc, char *argv[] )
{
   int tage, tag, monat, jahr, start;
   struct tm *zeit;
   time_t   sec;

   if (argc == 2 && strcmp(argv[1], "-i") == 0)
   {                                   /* interaktiv */
      printf("Jahr> ");scanf("%d", &jahr);
      printf("Monat> ");scanf("%d", &monat);
      printf("Tag (0 für kein bestimmter)> ");scanf("%d", &tag);
      if (tag == 0)
      {
         tag = 1;
         if (1 <= monat && monat <= 12 &&
             1582 < jahr && jahr < 3000)
         {
            tage = Anzahl_Tage( jahr, monat, 1);
            start = (tage + WOCHENTAG) % 7;
            printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
            return 0;
         }
         else
         {
            fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                            "1583 und 2999\n");
            return 2;
         }
      }
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000 &&
          1 <= tag && tag <= anzProMonat(jahr,monat))
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n"
                         "[Tag zwischen 1 und Anzahl für dieses Monat]\n");
         return 2;
      }
   }
   else if (argc == 3)                      /* Monat, Jahr */
   {
      monat = atoi(argv[1]);
      jahr = atoi(argv[2]);
      tag = 1;
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000)
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n");
         return 2;
      }
   }
   else if (argc == 4)                 /* ganzes Datum */
   {
      monat = atoi(argv[2]);
      jahr = atoi(argv[3]);
      tag = atoi(argv[1]);
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000 &&
          1 <= tag && tag <= anzProMonat(jahr,monat))
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n"
                         "[Tag zwischen 1 und Anzahl für dieses Monat]\n");
         return 2;
      }
   }
   else if (argc == 1)                 /* aktuelles Monat */
   {
      time(&sec);
      zeit = localtime(&sec);
      tag = zeit->tm_mday;
      monat = zeit->tm_mon + 1;
      jahr = zeit->tm_year + 1900;
      tage = Anzahl_Tage( jahr, monat, 1);
      start = (tage + WOCHENTAG) % 7;
      printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
      return 0;
   }
   else
   {
      fprintf(stderr, "Aufruf:\n%s [[tag] monat jahr]\n", argv[0]);
      return 1;
   }
}

/*-------------------------- functions ------------------------------*/
/** Kalender ausgeben
 *  start ... Wochentag des 1. dieses Monats
 *  anz   ... Anzahl der Tage dieses Monats
 *  heute ... Datum des heutigen Tages, 0, falls in einem anderen Monat
 *  monat ... Monat (1..12)
 *  jahr
 */
void printcal(int start, int anz, int heute, int monat, int jahr)
{
   char *tage[] = { "Montag", "Dienstag", "Mittwoch", "Donnerstag",
                    "Freitag", "Samstag", "Sonntag" };
   char *monate[] = { "Jänner", "Februar", "März", "April",
                      "Mai", "Juni", "Juli", "August",
                      "September", "Oktober", "November", "Dezember" };
   int i;
   int tag;

   printf("%s %d\n\n", monate[monat-1], jahr);
   for (i = 0; i < 7; i++)
   {
      printf("%10s ", tage[i]);
      for (tag = i + 1 - start; tag < anz + 7 + start; tag += 7)
         if (tag == heute && feiertag(jahr, monat, tag)
               && tag >= 1 && tag <= anz)
            printf("[%2d]", tag);
         else if (tag == heute && tag >= 1 && tag <= anz)
            printf("<%2d>", tag);
         else if (feiertag(jahr, monat, tag) && tag >= 1 && tag <= anz)
            printf(":%2d:", tag);
         else if (tag >= 1 && tag <= anz)
            printf("%3d ", tag);
         else
            printf("    ");
      printf("\n");
   }
   printf("\n");
   printFeiertage(jahr, monat);
}

/*
 * Function schaltjahr
 *
 * Zweck:   Liefert 1, wenn angegebenes Jahr ein Schaltjahr ist
 *
 * Algorithmus:
 *          ab 1582 gilt folgende Regel für Schaltjahre:
 *             Ist die Jahreszahl durch 400 teilbar oder
 *             ist die Jahreszahl durch 4 teilbar aber nicht durch 100
 *             dann ist das Jahr ein Schaltjahr
 *
 * Parameter:
 *       IN: Jahr
 *      OUT:
 *
 * Return-Wert: 1 ... Schaltjahr
 *              0 ... sonst
 */
int schaltjahr( int Jahr )
{
   if ( Jahr % 400 == 0 )
      return 1;
   else if ( Jahr % 100 == 0 )
      return 0;
   else if ( Jahr % 4 == 0 )
      return 1;
   else 
      return 0;
}

int anzProMonat(int jahr, int monat)
{
   switch(monat)
   {
      case 2:
         return 28 + schaltjahr(jahr);
         break;
      case 4:
      case 6:
      case 9:
      case 11:
         return 30;
         break;
      default:
         return 31;
   }
}

/*
 * Function Anzahl_Tage
 *
 * Zweck:    Bestimmt die Anzahl der Tage ab 1.1.1582
 *
 * Algorithmus:
 *           Tag + Anzahl Tage für Monate + Anzahl Tage für Jahre
 *
 * Parameter:
 *       IN: Jahr, Monat, Tag
 *      OUT:
 *
 * Return-Wert: Anzahl der Tage ab 1.1.1582
 */
int Anzahl_Tage( int Jahr, int Monat, int Tag )
{
   int   j;
   int   m;
   int   tage = Tag; /* Monatstag */

   /*
    * Tage für Jahre berechnen
    */
   for ( j = 1582; j < Jahr; j++ )
      tage += 365 + schaltjahr( j );

   /*
    * Tage für Monate berechnen
    */
   for ( m = 1; m < Monat; m++ )
      switch( m )
      {
         case 2:
            tage += 28 + schaltjahr( Jahr );
            break;
         case 4:
         case 6:
         case 9:
         case 11:
            tage += 30;
            break;
         default:
            tage += 31;
      }
   return( tage );
}

/** Bestimmt Feiertag.
 *  @param jahr
 *  @param monat
 *  @param tag
 *  @return Nummer des Feiertags, 0 sonst
 */
int feiertag(int jahr, int monat, int tag)
{
   int tagzahl, j, m, t;
   ostern(jahr, &m, &t);
   tagzahl = Anzahl_Tage(jahr, m, t);
   
   if (monat == m && tag == t)         /* Ostern */
      return OSTERSONNTAG;
   tag2datum(tagzahl + 1, &j, &m, &t);
   if (monat == m && tag == t)         /* Ostern */
      return OSTERMONTAG;
   tag2datum(tagzahl - 46, &j, &m, &t);
   if (monat == m && tag == t)         /* Aschermittwoch */
      return ASCHERMITTWOCH;
   tag2datum(tagzahl - 7, &j, &m, &t);
   if (monat == m && tag == t)         /* Palmsonntag */
      return PALMSONNTAG;
   tag2datum(tagzahl - 2, &j, &m, &t);
   if (monat == m && tag == t)         /* Karfreitag */
      return KARFREITAG;
   tag2datum(tagzahl + 49, &j, &m, &t);
   if (monat == m && tag == t)         /* Pfingstsonntag */
      return PFINGSTSONNTAG;
   tag2datum(tagzahl + 50, &j, &m, &t);
   if (monat == m && tag == t)         /* Pfingstmontag */
      return PFINGSTMONTAG;
   tag2datum(tagzahl + 56, &j, &m, &t);
   if (monat == m && tag == t)         /* Dreifaltigkeitssonntag */
      return DREIFALTIGKEITSSONNTAG;
   tag2datum(tagzahl + 60, &j, &m, &t);
   if (monat == m && tag == t)         /* Fronleichnam */
      return FRONLEICHNAM;

   if (monat == 1 && tag == 1)         /* Neujahr */
      return NEUJAHR;
   if (monat == 1 && tag == 6)         /* Heilige 3 Könige */
      return HL3KOENIGE;
   if (monat == 5 && tag == 1)         /* Staatsfeiertag */
      return STAATSFEIERTAG;
   tag2datum(sonntag(jahr, 5, 13), &j, &m, &t);
   if (monat == 5 && tag == t)         /* Muttertag FIXME */
      return MUTTERTAG;
   if (monat == 8 && tag == 15)        /* Mariahimmelfahrt */
      return MARIAHIMMELFAHRT;
   if (monat == 10 && tag == 26)       /* Nationalfeiertag */
      return NATIONALFEIERTAG;
   if (monat == 10 && tag == 31)       /* Reformationstag */
      return REFORMATIONSTAG;
   if (monat == 11 && tag == 1)        /* Allerheiligen */
      return ALLERHEILIGEN;
   if (monat == 11 && tag == 2)        /* Allersselen */
      return ALLERSEELEN;
   if (monat == 11)
   {
      if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
         tagzahl = sonntag(jahr, 12, 24) - 21;
      else
         tagzahl = sonntag(jahr, 12, 24) - 28;
      tag2datum(tagzahl, &j, &m, &t);
      if (tag == t && m == 11)         /* 1. Advent */
         return ADVENT1;
   }
   if (monat == 12)
   {
      if (tag == 8)                    /* Maria Emfängnis */
         return MARIAEMPFAENGNIS;

      if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
         tagzahl = sonntag(jahr, 12, 24) - 21;
      else
         tagzahl = sonntag(jahr, 12, 24) - 28;
      tag2datum(tagzahl, &j, &m, &t);
      if (tag == t && m == 12)         /* 1. Advent */
         return ADVENT1;
      tag2datum(tagzahl+7, &j, &m, &t);
      if (tag == t)                    /* 2. Advent */
         return ADVENT2;
      tag2datum(tagzahl+14, &j, &m, &t);
      if (tag == t)                    /* 3. Advent */
         return ADVENT3;
      tag2datum(tagzahl+21, &j, &m, &t);
      if (tag == t)                    /* 4. Advent */
         return ADVENT4;
      if (tag == 24)                   /* Hl. Abend */
         return HEILIGABEND;
      if (tag == 25)                   /* Christiani */
         return CHRISTIANI;
      if (tag == 26)                   /* Stefanitag */
         return STEFANI;
      if (tag == 31)                   /* Silvester :-) */
         return SILVESTER;
   }

   return KEIN;                        /* Kein Feiertag */
}

void ostern( int jahr, int *monat, int *tag )
{
   int a = jahr % 19;
   int b = jahr / 100;
   int c = jahr % 100;
   int d = b / 4;
   int e = b % 4;
   int f = ( b + 8 ) / 25;
   int g = ( b - f + 1 ) / 3;
   int h = ( 19 * a + b - d - g + 15 ) % 30;
   int i = c / 4;
   int k = c % 4;
   int l = ( 32 + 2 * e + 2 * i -h -k ) % 7;
   int m = ( a + 11 * h + 22 * l ) / 451;
   int p = ( h + l - 7 * m + 114 ) % 31;

   *monat = ( h + l - 7 * m + 114 ) / 31;
   *tag = p + 1;
}
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag )
{
   /*
    * Jahr berechnen
    */
   *jahr = 1582;
   while ( tagzahl > 365 + schaltjahr( *jahr ) )
   {
      tagzahl -= 365 + schaltjahr( *jahr );
      (*jahr)++;
   }
   /*
    * Monat
    */
   for ( *monat = 1; tagzahl > tagemonat( *jahr, *monat ); (*monat)++ )
      tagzahl -= tagemonat( *jahr, *monat );
   /*
    * Tag
    */
   *tag = tagzahl;
}

int sonntag( int jahr, int monat, int tag )
{
   int tagzahl = Anzahl_Tage( jahr, monat, tag );
   int wt = ( DONNERSTAG + tagzahl ) % 7;
   if ( wt == SONNTAG ) /* bereits Sonntag */
      return tagzahl;
   else
      return tagzahl + ( SONNTAG - wt );
}
int tagemonat( int jahr, int monat )
{
   switch( monat )
   {
      case 2:
         return 28 + schaltjahr( jahr );
         break;
      case 4:
      case 6:
      case 9:
      case 11:
         return 30;
         break;
      default:
         return 31;
   }
}

/** gib eine Liste der Feiertage aus.
 *  @param jahr IN
 *  @param monat OUT
 */
void printFeiertage(int jahr, int monat)
{
   int i;
   char *bez = "";
   for (i = 1; i <= tagemonat(jahr, monat); i++)
   {
      bez = "";
      switch (feiertag(jahr, monat, i))
      {
         case NEUJAHR:
            bez = "Neujahr";
            break;
         case HL3KOENIGE:
            bez = "Heilige Dreikönige";
            break;
         case ASCHERMITTWOCH:
            bez = "Aschermittwoch";
            break;
         case PALMSONNTAG:
            bez = "Palmsonntag";
            break;
         case KARFREITAG:
            bez = "Karfreitag";
            break;
         case OSTERSONNTAG:
            bez = "Ostersonntag";
            break;
         case OSTERMONTAG:
            bez = "Ostermontag";
            break;
         case STAATSFEIERTAG:
            bez = "Staatsfeiertag";
            break;
         case MUTTERTAG:
            bez = "Muttertag";
            break;
         case PFINGSTSONNTAG:
            bez = "Pfingstsonntag";
            break;
         case PFINGSTMONTAG:
            bez = "Pfingstmontag";
            break;
         case DREIFALTIGKEITSSONNTAG:
            bez = "Dreifaltigkeitssonntag";
            break;
         case FRONLEICHNAM:
            bez = "Fronleichnam";
            break;
         case MARIAHIMMELFAHRT:
            bez = "Maria Himmelfahrt";
            break;
         case NATIONALFEIERTAG:
            bez = "Nationalfeiertag";
            break;
         case REFORMATIONSTAG:
            bez = "Reformationstag";
            break;
         case ALLERHEILIGEN:
            bez = "Allerheiligen";
            break;
         case ALLERSEELEN:
            bez = "Allerseelen";
            break;
         case MARIAEMPFAENGNIS:
            bez = "Maria Empfängnis";
            break;
         case ADVENT1:
            bez = "1. Advent";
            break;
         case ADVENT2:
            bez = "2. Advent";
            break;
         case ADVENT3:
            bez = "3. Advent";
            break;
         case ADVENT4:
            bez = "4. Advent";
            break;
         case HEILIGABEND:
            bez = "Heiligabend";
            break;
         case CHRISTIANI:
            bez = "Christtag";
            break;
         case STEFANI:
            bez = "Stefanitag";
            break;
         case SILVESTER:
            bez = "Silvester :-)";
            break;
      }
      if (strlen(bez) > 0)
         printf("%2d.%d. %s\n", i, monat, bez);
   }
}

Labels: , ,


This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]