Notatki

Autor: Wojciech Muła

Strona główna

Table of contents

2012

Ada: deklaracja jednoelementowej tablicy [2012-07-07]

x : Type(1 .. 1) := (1 => initial_value);

2010

Opis biblioteki pthreads na Wikibooks [31.03.2010]

W ramach głębszego poznawania biblioteki pthreads utworzyłem w serwisie wikibooks opis POSIX Threads, zawierający bardzo podstawowe informacje o większości funkcji tej biblioteki oraz nieco o rozszerzeniach Linuxowej implementacji. Integralną częścią są przykładowe programy.

(Wikibooks niestety nie zyskał takiej popularności, jak Wikipedia, a to całkiem wygodna platforma do tworzenia różnego rodzaju podręczników).

2008

AltiVec: ciekawe rozwiązanie [25.04.2008]

AltiVec definiuje rejestr znaczników w którym aplikacja markuje rejestry wykorzystywane przez siebie (rejestrów jest 32), natomiast system operacyjny wykorzystuje te informacje przy przełączaniu kontekstu. Bardzo pomysłowe.

MMX/SSE — wykrywanie przeniesień [20.04.2008]

Inspiracja rozkazami AltiVec, które umożliwiają zwrócenie wektora bitów przeniesień z dodawania/odejmowania, tj. (dla wektora bajtów):

for i:=0 to 15 do
        dst[i] = (src[i] + dst[i] > 255) ? 1 : 0

Realizacja MMX/SSE wymaga trochę więcej zachodu:

; xmm0, xmm1    - argumenty
; xmm2          - rej. pomocniczy

movdqa  %xmm0, %xmm2
paddb   %xmm1, %xmm0    ; suma modulo 256
paddusb %xmm1, %xmm2    ; suma z nasyceniem
pcmpeqb %xmm2, %xmm0    ; 0xff dla tych bajtów, dla których
                        ; nie wystąpiło nasycenie
pcmpeqb %xmm2, %xmm2    ; xmm2 := packed_byte(0xff)
psubb   %xmm2, %xmm0    ; xmm0 -- wynik

PTEST — alternatywy? [1.04.2008]

Dla rozkazu PTEST (SSE4) wiadomo już, że opóźnienie wynosi 1 cykl (patrz najnowsza dokumentacja Intela). Problem z PTEST polega na tym, że jest on dość, hmm... prymitywny.

Wymyśliłem ostatnio trochę inne rozkazy, które wpływałyby na rejestr EFLAGS: nazwijmy jest PTESTANY i PTESTALL.

Oba przyjmują trzy argumenty:

  • rejestr XMM (nie jest modyfikowany),
  • 8-bitowa stała natychmiastowa imm8,
  • 16-bitowa stała natychmiastowa imm16 — maska.

Stała imm8 wybiera bit 0..7 — ze wszystkich bajtów XMM tworzone słowo 16-bitowe word16 wykorzystując wskazany bit (czyli taki bardziej ogólny PMOVMSKB).

Następnie dla rozkazu PTESTANY sprawdza się czy word16 AND imm16 jest niezerowe — tj. przynajmniej jeden niezamaskowany bit ustawiony. Natomiast dla rozkazu PTESTALL, sprawdza się czy word16 OR imm16 jest równe 0xffff — tj. wszystkie niezamaskowane bity ustawione.

Wynik testu byłby przepisywany np. do CF. Dodatkowo można jeszcze sprawdzać czy word16 = 0 i wykonywać testy bez maskowania. W sumie rozkazy ustawiałyby trzy flagi:

mask   := conv(imm8);           { maska bitowa }
bit    := 0x0001;
word16 := 0x0000;

{ utworzenie słowa }
for i := 0 to 16 do
   begin
        if XMM[i] AND mask then
                word16 := word16 or bit;

        bit := bit SHL 1;
   end;

{ PANY }
ZF := (word16 = 0);
CF := (word16 AND imm16 <> 0);
SF := (word16 <> 0);

{ PAND }
ZF := (word16 = 0);
CF := (word16 OR imm16 = 0xffff);
SF := (word16 = 0xffff);

2007

Wyciągnięcie wszystkich słów ze słownika aspella [15.06.2007]

aspell dump master lang_code | aspell expand | tr " " "\n"

Python (4): reduce [15.03.2007]

Pewien anonimowy wykładowca miał powiedzieć, iż za pomocą pętli for można zrobić wszystko, może za wyjątkiem dzieci. Więc ja uważam, że jeśli chodzi o pythonową funkcję reduce (znaną w językach funkcyjnych jako foldl, tj. fold left) to za jej pomocą można wszystko, łącznie z dziećmi, a nawet bliźniakami.

Poniżej garść przykładów, m.in. funkcja groupby — którą kiedyś potwornie umordowałem na potrzeby pydvi2svg — teraz jest krótka, prosta, wydajna.

reduce-samples.py:

# wm, 14.03.2007

def groupby(seq, value=lambda x: x):
        def aux((vp, L), curr):
                vc = value(curr)
                if vp == vc:
                        L[-1].append(curr)
                else:
                        L.append([curr])
                return (vc, L)
        
        return reduce(aux, seq, (aux, []))[1]


def groupby2(seq, value=lambda x: x):
        def aux((vp, L), curr):
                vc = value(curr)
                if vp == vc:
                        L[-1][1].append(curr)
                else:
                        L.append( (vc, [curr]) )
                return (vc, L)
        
        return reduce(aux, seq, (aux, []))[1]


def groupby3(seq, cmpfun=lambda x, y: x == y):
        def aux((prev, L), curr):
                if cmpfun(prev, curr):
                        L[-1].append(curr)
                else:
                        L.append([curr])
                return (curr, L)
        
        return reduce(aux, seq, (aux, []) )[1]


def flatten(seq):
        def aux(L, Li):
                L.extend(Li)
                return L
        return reduce(aux, seq, [])


def partition(seq, pred=bool):
        def aux((L1, L2), item):
                if pred(item):
                        L1.append(item)
                else:
                        L2.append(item)
                return (L1, L2)

        return reduce(aux, seq, ([], []))


def unzip2(seq):
        def aux((L1, L2), (x1, x2)):
                L1.append(x1)
                L2.append(x2)
                return (L1, L2)

        return reduce(aux, seq, ([], []))

Python zamiast XSLT [9.01.2007]

Będzie to kolejna opowieść z serii: „W Pythonie można wiele rzeczy zrobić szybko i wygodnie, a nawet szybciej i wygodniej niż w X”.

Kiedyś napisałem dość spory skrypt XSLT tłumaczący XML z glosariuszem na wzajemnie połączone pliki HTML. Całkiem fajnie to wyszło, ale już podział na odrębne pliki musiałem robić zewnętrznym skryptem w AWK. Później miałem jeszcze dorobić drugi skrypt XSLT-owy, który tłumaczyłaby ów XML na LaTeX-a, ale dałem sobie spokój, bo było tam parę rzeczy do rozwiązania, między innymi automatyczne zapobieganie pojawianiu się sierot. W sumie można było znów dopisać jakieś narzędzie w AWK, ale rozmyśliłem się i ostatecznie postanowiłem zrobić wszystko w Pythonie. Chociaż rozmiar XSLT-owego skryptu (10kB, nieco ponad 1000 wierszy) trochę odstraszał — komu się chce robić drugi raz to samo...

W zasadzie kluczowym elementem, decydującym o sukcesie, była jedna funkcja (a w zasadzie funkcyjka), która automatycznie przechodziła dokument XML-owe wszerz i wywoływała zarejestrowane funkcje przekształcające zawartość konkretnych tagów:

def apply(element, tags=None):
    result = ”
    try:
        if not tags:
            for child in element.childNodes:
                result += __callbacks[child.nodeName](child)
        else:
            for child in element.childNodes:
                name = child.nodeName
                if name == '#comment': pass
                elif name in tags or name == '#text':
                    result += __callbacks[name](child)
    except KeyError, e:
        log.error("Handler for <%s> undefined." % e)
        raise KeyError(str(e))
    return result

Jest to mniej więcej odpowiednik XSLT-owego match — nie ma tylko argumentu mode, którego nie potrzebowałem.

Słownik callbacks zawiera odwzorowanie nazwa taga -> funkcja [zwracająca łańcuch znaków]. Wszystkie funkcje nazywałem wg schematu element_xxx gdzie xxx to nazwa taga i wówczas ich rejestracja w Pythonie okazała się banalna:

def register_all(namespace, prefix='element_'):
    global __callbacks
    for name, object in namespace.iteritems():
        if not name.startswith(prefix): continue
        if not callable(object): continue

        tag = name[len(prefix):]
        __callbacks[tag] = object

Dzięki function factory („generatory funkcji”?) tworzenie funkcji transformujących było banalne. Oto kawałek kodu dokonującego przekształcenia XHTML na LaTeX-a (we wspomnianym glosariuszu używam XHTML-a plus kilka dodatkowych tagów):

def repl(start_tag, end_tag, tags=None):
    "<tag>...</tag> -> start_tag + ... + end_tag"
    def element_fun(element):
        return start_tag + apply(element, tags) + end_tag
    return element_fun

def tex(name, tags=None):
    "<tag>text</tag> => \name{text}"
    return repl("\\" + name + "{", "}", tags)

def texenv(name, tags=None):
    return repl("\\begin{"+name+"}\n", "\\end{"+name+"}\n", tags)

element_b = tex('textbf')
element_i = tex('textit')
element_u = tex('underline')
element_center = texenv('center')
element_blockquote = texenv('quotation')
# itd.
# w zasadzie tylko kilka funkcji element_xxx musiałem
# "ręcznie wklepywać"

Ostatecznie wyszło dwa razy mniej kodu niż w XSTL, programy są o wiele prostsze i wszystkie rzeczy (podział na pliki, jakieś dodatkowe transformacje tekstu) pozałatwiane w jednym miejscu.

Jak to ktoś ładnie napisał na temat Pythona: „Who Needs the Toolbox When You Have a Toolmaker?” (podkreślenie moje).

LaTeX (4) — obrazek na całą stronę [8.01.2007]

Swego czasu robiłem wersję elektroniczną pewnej publikacji i należało jakoś wstawić do PDF-a dwie ilustracje (skan okładki i coś jeszcze) jako całe strony.

Udało się to uzyskać z użyciem standardowego LaTeXowego środowiska picture z obrazkiem przeskalowanym do rozmiarów \pagewidth/\pageheight. Jedyny problem, który z początku wydawał się „problemikiem na pół kwadransa”, polegał na dosunięciu obrazka do lewego górnego rogu strony. Przyznaję, że wówczas zrobiłem to „najszlachetniejszą matematyczną metodą”, czyli metodą prób i błędów.

Jednak po pewnym czasie zagłębiłem się w „latechową marginesologię”, czego efektem jest polecenie \imageaspage, które wstawia wskazany obrazek jako stronę; wydaje się, że robi to dobrze niezależnie od ustawionych marginesów.

WYSIWYG w procesorach tekstu [7.01.2007]

Obserwując w jaki sposób mniej lub bardziej popularne procesory tekstu (MS Word, OpenOffice, AbiWord) składają tekst, uważam że w ich przypadku skrót WYSIWYG powinno się tłumaczyć jako „Na wydruku będzie tak samo brzydko, jak na ekranie”.

Tkinter Events Serializer oparte na wątkach [5.01.2007]

Tkinter Events Serializer został uaktualniony.

Dostępne są teraz dwie klasy realizujące szeregowanie zdarzeń:

  • EventsSerializerTk — oparte na mechanizmach Tkintera
  • EventsSerializerThreads — oparte na wątkach

Klasa EventsSerializer jest aliasem albo dla klasy „wątkowej” (jeśli dostępne są moduły Queue oraz thread) albo „tkinterowej” (w przeciwnym razie).

fbi_text [4.01.2007]

fbi_text to program proof-of-concept, którego zadaniem jest pokazanie, że w Linuksie można wszystko, tylko nie wszystko jeszcze zostało napisane.

fbi_text wyświetla czarno-białe obrazki bezpośrednio na konsoli, podmieniając na chwilę fonty, tak żeby wyświetlone obok siebie znaczki reprezentowały obraz. Na kartach EGA/VGA w standardowym trybie tekstowym 80x25 znaków glify mają rozmiar 8x16 pikseli, dostępnych znaków jest przynajmniej 256[1], więc jeśli wyświetlimy te 256 znaków w 16 wierszach mamy obraz 128x256 pikseli i tak też ustawiłem w fbi_text.

[1] Dostępne jest 512, ale mnie się nie udało tylu użyć i w sumie nie zagłębiałem się w to dokładnie.

Jednak karty EGA/VGA domyślnie nie wyświetlają znaków o szerokości 8 pikseli, ale 9. W większości przypadków 9. kolumna jest pusta (dodatkowy odstęp) ale dla znaków z zakresu 0xC0-0xDF 9. kolumna jest powieleniem ostatniej (semigrafika). Dlatego niestety obrazek będzie „pocięty” pionowymi czarnymi kreskami.

Ale nie wszystko tak do końca stracone — jeśli masz dostęp do konta roota, uruchom program z opcją „root”, wówczas jeśli będzie to możliwe program spróbuje przeprogramować kartę tak, żeby znaki miały naprawdę 8 pikseli szerokości i wówczas zobaczymy niezakłócony obraz.

Do pobrania:

Kompilacja:

gcc -O2 view_img.c -o view_img

screen [3.01.2007]

Screen jest bardzo użytecznym narzędziem i ma pewną cechę, która może być użyteczna w programach.

Mianowicie screen rozpoznaje Privacy Messages — sekwencję sterującą ESC^tekstESC\, która pojawienie się powoduje wyświetlenie w wierszu statusu podanego tekstu. (Niby fajnie, ale niestety na czas wyświetlania komunikatu nasz program jest blokowany...)

Żeby stwierdzić, czy program jest uruchomiony z poziomu screena, należy sprawdzić, czy jest ustawiona zmienna środowiskowa STY.

LaTeX (3) — „zen of \ldots” [1.01.2007]

Czyli jak oznaczyć opuszczenie cytatów i nie zrobić tego źle:

img/ldots.png

(Stosowanie „(...)” jeśli nawet nie jest błędem, powoduje dyskomfort estetyczny).

2006

Ocaml (3) - Hashtbl [4.06.2006]

Ocamlowy moduł Hashtbl oferuje minimum funkcjonalności. Dopisałem więc kilka funkcji, wzorując się na metodach pythonowego słownika.

W pliku myhash.ml znajdują się następujące funkcje:

  • itertop — iteracja tylko po pierwszych wartościach (zwracanych przez Hashtbl.find)
  • foldtop — składanie tylko pierwszych wartości
  • values — lista wartości
  • keys — lista kluczy
  • items — lista par (klucz:wartość)
  • from_items — utworzenie hasha z list kluczy i wartości
  • from_items2 — utworzenie hasha z listy par (klucz, wartość)
  • findVariant — jak Hashtbl.find, ale zwraca wartość typu Option zamiast rzucać wyjątkiem
  • set_default — zwraca wartość klucza; jeśli klucza nie ma, to przypisywana jest mu wartość domyślna
  • remove_all — usuwa wszystkie wartości związane z kluczem
  • replace_all — zastępuje wszystkie wartości związane z kluczem jedną wartością
  • reverse_hash — odwraca znaczenie kluczy i wartości

Ocaml (1) - łańcuchy znaków [4.06.2006]

Dwa lata temu, dzięki wykładom prof. Trybusa miałem okazję zapoznać się z językiem Standard ML i co tu dużo mówić, po prostu zakochałem się w stylu funkcyjnym — jest taki elegancki, taki zwięzły... Niestety, na pewien czas zarzuciłem SML-a, ale ostatnio rozczarowany powolnością Pythona postanowiłem wrócić do języków funkcyjnych, konkretnie do Ocamla.

Ocaml (jak i inne ML-e) jest językiem statycznie typowanym z typami polimorficznymi, inferencją typów, dopasowaniem wzorców (pattern matching), porządnym systemem modułów, wsparciem dla programowania obiektowego oraz imperatywnego i wreszcie porządnym kompilatorem.

Jednak jego biblioteka String jest stosunkowo uboga, a ja się niestety przyzwyczaiłem do bogactwa Pythonowego stringa. Dlatego w ramach poznawania języka postanowiłem napisać parę (moim zdaniem) brakujących funkcji.

Oto lista funkcji z pliku mystring.ml (11kB); niektóre występują w kilku wariantach, ale nie będę się rozdrabniać:

  • isalpha, isdigit, itd. — funkcje znane z języka C, stwierdzające przynależność znaku do danej klasy
  • isalpha_s, isdigit_s, itd. — j.w., ale stwierdzają przynależność wszystkich znaków łańcucha do danej klasy
  • lstrip — wycięcie z początku łańcucha niepożądanych znaków
  • rstrip — wycięcie z końca łańcucha niepożądanych znaków
  • strip — wycięcie z początku i końca łańcucha niepożądanych znaków (szybsze niż złożenie lstrip, rstrip)
  • split — podział łańcucha (na określonych znakach) na listę podłańcuchów
  • splitn — j.w., ale dokonywane jest n pierwszych podziałów
  • expandtabs — rozwinięcie tabulatorów
  • strstr — wyszukanie pierwszego wystąpienia łańcucha (wzorca) w innym łańcuchu znaków (używany algorytm Karpa-Rabina z uproszczoną funkcją mieszającą)
  • strstrall — wyszukanie wszystkich wystąpień wzorca
  • filter, map, fold_left — standardowe funkcje wyższego rzędu
  • toupper, tolower, swapcase — zmiana wielkości znaków w łańcuchu
  • toupper_c, tolower_c, swapcase_c — zmiana wielkości znaku
  • translate — translacja znaków w łańcuchu (wygodne do utworzenia np. funkcji toupper, uwzględniającej znaki narodowe)
  • translate_c — translacja znaku
  • concat' — nieco szybsze od List.concat "" lista
  • startswith, endswith — stwierdzenie, czy łańcuch jest początkiem/końcem innego łańcucha

Przykład wykorzystania tych funkcji:

let capitalize str =
        let _ = fold_left (fun (prevspace,i) c ->
                           let space = isspace c in
                               if prevspace=true && space=false then
                                  str.[i] <- toupper_c c;
                           (space, i+1)
                          ) (true, 0) str
        in str

let haxorstring str =
        let _ = fold_left (fun (l,i) c ->
                           if l then str.[i] <- toupper_c c
                                else str.[i] <- tolower_c c;
                           (not(l), i+1)
                          ) (true, 0) str
        in str

let pl_lower = "abcdefghijklmnopqrstuvwxyząćęłńóśżź"
and pl_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZĄĆĘŁŃÓŚŻŹ"
let pl_alpha = make_cset (pl_lower ^ pl_upper)

let isalpha  = in_cset pl_alpha

let pl_toupper_tbl  = make_conv_table pl_lower pl_upper
and pl_tolower_tbl  = make_conv_table pl_upper pl_lower
and pl_swapcase_tbl = make_conv_table (pl_upper^pl_lower) (pl_lower^pl_upper)

let toupper  = translate pl_toupper_tbl
let tolower  = translate pl_tolower_tbl
let swapcase = translate pl_swapcase_tbl

I przykładowa sesja:

# capitalize "sample capitalized text";;
- : string = "Sample Capitalized Text"
# haxorstring "samPLe haXORed StRing";;
- : string = "SaMpLe hAxOrEd sTrInG"


# let s1 =  "Zażółć Gęślą Jaźń";;
val s1 : string = "Zażółć Gęślą Jaźń"
# tolower s1;;
- : string = "zażółć gęślą jaźń"
# toupper s1;;
- : string = "ZAŻÓŁĆ GĘŚLĄ JAŹŃ"
# swapcase s1;;
- : string = "zAŻÓŁĆ gĘŚLĄ jAŹŃ"

Ocaml (2) - listy [3.06.2006]

Jak napisałem wcześniej Ocamlowy moduł String jest ubogi, nie lepiej jest z modułem List. Żeby nie było, że teraz narzekam (i narzekałem wcześniej) — rozumiem, iż twórcy Ocamla chcą, aby podstawowa biblioteka była jak najmniejsza.

Napisanie tych dodatkowych funkcji zajęło mi naprawdę niewiele czasu i pozwoliło trochę lepiej zapoznać się z językiem i jego bibliotekami (i przede wszystkim była to bardzo przyjemne).

W pliku mylist.ml dostępne są następujące funkcje, część znana z SML-a:

  • explode, implode (w FAQ implode jest napisane lepiej)
  • last — ostatni element listy
  • allbutone — lista bez ostatniego elementu
  • allbut — lista bez n ostatnich elementów
  • take — n pierwszych elementów
  • drop — bez n pierwszych elementów
  • cut — elementy od k-tego do n-tego
  • splitat — podział listy na dwie, na n-tym elemencie
  • change — zmiana n-tego elementu
  • insertat — wstawienie za n-ty element nowego elementu
  • index — zwraca indeks pierwszego wystąpienia podanej wartości
  • rindex — zwraca indeks ostatniego wystąpienia podanej wartości
  • range — zwraca listę liczb całkowitych [lo... hi)
  • tabulate — aplikacja funkcji dla wartości od lo do hi
  • imap, ifilter — jak map/filter, ale funkcja dostaje również indeks elementu
  • count — obliczenie ilości wystąpień danego elementu na liście

W pliku mylist2.ml dostępne są niektóre funkcje z Haskellowego modułu Data.List:

  • intersparse — wstawienie między wszystkie elementy listy nowego elementu
  • dup — tworzenie listy
  • takeWhile — początek listy składający się z elementów dla których predykat jest prawdziwy
  • dropWhile — lista bez początkowych elementów dla których predykat jest prawdziwy
  • group — grupowanie powtarzających się elementów w listy
  • inits — wszystkie prefiksy listy
  • tails — wszystkie sufiksy listy
  • isPrefixOf — stwierdzenie, czy lista jest prefiksem innej listy
  • isSuffixOf — stwierdzenie, czy lista jest sufiksem innej listy
  • elemIndex — indeks pierwszego elementu dla którego predykat jest prawdziwy
  • elemIndices — lista indeksów wszystkich elementów dla których predykat jest prawdziwy
  • nub — pozostawienie na liście tylko jednego elementu spełniającego predykat
  • delete — usunięcie wszystkich elementów spełniających predykat
  • deleteElem — usunięcie wszystkich elementów określonej wartości

Bardziej zaawansowane makra w LaTeX-u (2) [19.05.2006]

Ograniczenia na liczbę argumentów makra można „złamać” korzystając z wcześniej opisanych własności plain-TeX-owych oraz rekurencji. Tak, TeX potrafi rekurencyjnie rozwijać makra (do chwili wyczerpania pamięci).

Pokażę jak można podać jako argument dowolnie długą listę elementów oddzielonych przecinkami; rzecz jasna można użyć innych znaku/znaków do rozdzielania elementów.

W tym celu trzeba zdefiniować dwa makra: jedno które będzie rozwijało się rekursywnie, oraz drugie (główne) inicjujące rekursję — wywołujące pierwsze makro z odpowiednimi parametrami:

\def\foorec#1,#2:{%
        — `#1'\par
        \def\tempa{}%
        \def\tempb{#2}%
        \ifx\tempa\tempb
        \else
                \foorec#2:
        \fi
}

\def\foo#1{\foorec#1,:}

Makro \foorec rozbija argument na dwie części: #1 przechowuje głowę listy (ciąg znaków ograniczony przecinkiem), natomiast #2 ogon listy (ciąg znaków ograniczony dwukropkiem, wybranym tutaj z braku lepszego pomysłu). Na początku makro wykonuje działanie na głowie listy (#1): w tym przypadku wypisuje ją w nowym akapicie poprzedzając pauzą.

Następnie jeśli ogon (#2) jest pusty, to rekursja w tym miejscu się kończy. Jeśli nie, wywoływane jest makro \foorec z argumentem będącym ogonem listy.

Zadaniem makra \foo jest ograniczenie listy (wstawienie dwukropka) oraz wstawienie na koniec listy pustego elementu (gwarantującego zakończenie rekursji).

Przykład wykorzystania (12 argumentów):

\foo{one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve}

Bardziej zaawansowane makra w LaTeX-u (1) [19.05.2006]

Ludzie używający LaTeX-a chyba nieczęsto sięgają do plain-TeX-owych konstrukcji. A czasem warto. W przypadku definiowania funkcji używamy polecenia \newcommand lub \renewcommand — TeX-owa konstrukcja \def daje więcej możliwości.

Rozdzielając argumenty makra definiowanego przy użyciu \def ciągami znaków zmuszamy TeX-a do bardziej zaawansowanego parsowania:

\def\bold#1:{\textbf{#1}:\ }

\bold Pytanie: kiedy Python jest lepszy? \bold Odpowiedź: Zawsze!

Makro \bold jako argument weźmie cały ciąg do znaku dwukropka („zjadając” dwukropek). To tylko prosty przykład, ale już pokazuje, że nie zawsze trzeba męczyć się z klamerkami.

Weźmy inny przykład, bardziej skomplikowany, w którym data zapisana w stylu anglosaskim (rok/miesiąc/dzień) jest zamieniana na polski zapis (dzień.miesiąc.rok) - uwaga, istotna jest spacja po #3:

\def\rdate#1/#2/#3 {#3.#2.#1 }

Od \rdate 2006/05/17 do \rdate 2006/05/19 pada deszcz.

Sumując: dzięki zastosowaniu \def można w niektórych przypadkach znacząco uprościć sobie robotę.

Geometria komputerowa [2.05.2006]

Zapewne każda osoba zainteresowana grafiką komputerową, w szczególności krzywymi i powierzchniami parametrycznymi zna klasyczną już książkę dra Przemysława Kiciaka Podstawy modelowania krzywych i powierzchni (WNT, 2005). W sieci znalazłem dwie niezwykle interesujące pozycje w tym temacie, które wg mnie są doskonałym uzupełnieniem i rozszerzeniem książki:

Polecam również uwadze wyśmienity modeler rozpowszechniany na licencji BSD Ayam autorstwa Randolfa Schultza.

Uroki CISC [2.05.2006]

Postanowiłem policzyć częstość występowania poszczególnych rozkazów procesora w statystycznym programie (zdisabemblowałem większość programów z /usr/bin). Zapraszam do zapoznania się z wynikami. Proszę zobaczyć ile instrukcji występuje ledwie kilkadziesiąt, czy nawet kilka razy! A niektórych instrukcji z listy rozkazów x86 w ogóle nie ma!

Co to znaczy? Ano że niektóre rozkazy są albo całkowicie zbędne, albo że można się bez nich objeść. Zauważono to kilkanaście lat temu, co stało się impulsem do stworzenia architektury RISC, której główną cechą była zredukowana liczba rozkazów, ograniczona tylko do rozkazów najbardziej podstawowych i potrzebnych.

Więcej o tej architekturze na Wikipedii.

Szybkie stwierdzenie, czy 0.0 <= t <= 1.0 [7.04.2006]

GCC kompiluje powyższe wyrażenie do czegoś takiego:

xor     edx, edx
fld     [x]
fldz
fucom   st1
fnstsw  ax
sahf
jb      zero

fld1
fucompp st1
fnstsw  ax
sahf
jb      label
mov     ebx, byte 1
jump    one:

zero:
fstp    st0

one:
mov     eax, edx
ret

Pytanie jest następujące — czy można prościej? Odpowiedź: tak, wykorzystując operacje całkowitoliczbowe.

  1. Jeśli bit znaku jest ustawiony, wówczas liczba jest ujemna, a więc t jest spoza rozpatrywanego zakresu. Uwaga! W ten sposób nie uwzględniamy -0.0. Ale można po prostu stwierdzić na początku, czy mamy do czynienia z jedną z wartości specjalnych.
  2. Jeśli wykładnik jest mniejszy od zera, to moduł liczby jest mniejszy od 1. A więc jeśli jest odwrotnie (większy od zera), wówczas na pewno liczba większa od 1. Jeśli wykładnik jest równy zero, to tylko dla wartości 1.0 wynik porównania winien być prawdziwy.

Program:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

int myrange01(float x)
{
 uint32_t bin   = *(uint32_t*)&x;

 if ((bin & 0x7fffffff) == 0)           // x = +/-0.0
         return 1;
 if ((bin & 0x80000000) == 0x80000000)  // x < 0
         return 0;
 if (bin > 0x3f800000)                  // exp > 1.0
         return 0;
 else
         return 1;
}

Dygresja: proszę zauważyć — mamy cudowny Pentium IV złożony z kilku milionów tranzystorów, mamy nowoczesne instrukcje MMX, SSE, SSE2, a nawet SSE3. I pomimo tak wielkiego postępu nikt w Intelu nie wpadł na pomysł, że może byłoby dobrze dodać instrukcje porównujące liczby zmiennoprzecinkowe, które bezpośrednio modyfikowałyby flagi ALU. W XXI wieku trzeba robić takie chore sztuczki, po prostu paranoja!