Zdecydowałem o zamknięciu tego bloga, na korzyść dwóch nowych, które zamierzam otworzyć.
Powodów jest kilka:
1. Ten blog miesza rzeczy osobiste (głównie pierwsze wpisy) z wpisami typowo technicznymi. Osoby zainteresowane jednymi niekoniecznie będą zainteresowane drugimi, i odwrotnie.
2. Jest on napisany po polsku, co uniemożliwia (ok, utrudnia) wielu anglojęzycznym czytelnikom dotarcie do mojego bloga. Nie oszukujmy się, niewielu jest w Polsce programistów zainteresowanych Haskellem...
3. Zmiana języka na angielski byłaby z kolei ciosem dla tych osób, które znają mnie osobiście i dla których czytanie o np. ostatniej wyprawie w góry jest bardziej naturalne w języku ojczystym. Tak jest mi z resztą wygodniej.
4. Wreszcie denerwuje mnie sam blogger: jest w nim mnóstwo niedociągnięć które utrudniają codzienne użytkowanie tej platformy. Z tego względu nowe blogi będą znajdowały się na serwisie wordpress.com
Bloga osobistego jeszcze nie założyłem.
Blog techniczny dostępny jest pod adresem http://mostlycode.wordpress.com
sobota, sierpnia 01, 2009
wtorek, marca 31, 2009
Odtwarzanie muzyki z linii komend
Bardzo często zdarza mi się odtwarzać muzykę mplayerem. Jest to odtwarzacz konsolowy, co sprawia pewne kłopoty. Brakuje mu także opcji odtwarzania wszystkich utworów z danego katalogu i podkatalogów.
Oto rozwiązanie tego problemu za pomocą narzędzia find:
$ find -type f -printf "`pwd`/%p\n" | shuf > /tmp/pls && mplayer -playlist /tmp/pls
Kilka uwag:
- używam komendy "shuf" aby zrandomizować listę odtwarzanych utworów. mplayer posiada opcję -shuffle, ale powoduje ona losowanie przy każdej zmianie utworu. shuf ustala jedną permutację listy, dzięki czemu można się cofnąć do odegranego już utworu
- lista tworzona jest w /tmp/pls. Można uprościć polecenie find jeżeli zapiszemy ją w aktualnym katalogu:
$ find -type f | shuf > pls && mplayer -playlist pls
Ja nie chcę jednak sobie zaśmiecać katalogów playlistami.
Można wreszcie nie używać pliku pomocniczego, a korzystać z przekierowania strumieni:
$ find -type f | shuf | mplayer -playlist -
Wtedy jednak nie można sterować mplayerem, np. zmniejszając głośność lub zmieniając utwór. W tym momencie shuf staje się zbędny.
$ find -type f | mplayer -shuffle -playlist -
Na dobrą sprawę możemy pominąć także parametr "-type f" który mówi find by wyświetlał jedynie pliki, a pomijał katalogi.
$ find | mplayer -shuffle -playlist -
W ostateczności można też słuchać plików w kolejności podawanej przez find:
$ find | mplayer -playlist -
I tak wygląda komenda od której zacząłem :-)
Oto rozwiązanie tego problemu za pomocą narzędzia find:
$ find -type f -printf "`pwd`/%p\n" | shuf > /tmp/pls && mplayer -playlist /tmp/pls
Kilka uwag:
- używam komendy "shuf" aby zrandomizować listę odtwarzanych utworów. mplayer posiada opcję -shuffle, ale powoduje ona losowanie przy każdej zmianie utworu. shuf ustala jedną permutację listy, dzięki czemu można się cofnąć do odegranego już utworu
- lista tworzona jest w /tmp/pls. Można uprościć polecenie find jeżeli zapiszemy ją w aktualnym katalogu:
$ find -type f | shuf > pls && mplayer -playlist pls
Ja nie chcę jednak sobie zaśmiecać katalogów playlistami.
Można wreszcie nie używać pliku pomocniczego, a korzystać z przekierowania strumieni:
$ find -type f | shuf | mplayer -playlist -
Wtedy jednak nie można sterować mplayerem, np. zmniejszając głośność lub zmieniając utwór. W tym momencie shuf staje się zbędny.
$ find -type f | mplayer -shuffle -playlist -
Na dobrą sprawę możemy pominąć także parametr "-type f" który mówi find by wyświetlał jedynie pliki, a pomijał katalogi.
$ find | mplayer -shuffle -playlist -
W ostateczności można też słuchać plików w kolejności podawanej przez find:
$ find | mplayer -playlist -
I tak wygląda komenda od której zacząłem :-)
niedziela, lutego 22, 2009
Instancja klasy Show dla funkcji
Wbrew pozorom łatwo napisać taką instancję. Nie będzie ona spełniała warunku
> (read . show) == id
ale może być użyteczna. Można ją znaleźć w przypadku GHC 6.10 w module Text.Show.Functions. Wygląda ona tak:
instance Show (a -> b) where
showsPrec _ _ = showString "<function>"
Dzisiaj wpadłem na nieco pożyteczniejszą formę. Niestety, wszystko ma swoją cenę - zadziała ona tylko dla funkcji których argumenty są elementami klasy Typeable.
instance (Typeable a, Typeable b) => Show (a -> b) where
show _ = "<function: " ++ show (typeOf (undefined :: a)) ++ " -> " ++ show (typeOf (undefined :: b)) ++ ">"
Przykładowe działanie:
*Main> print print
Loading package syb ... linking ... done.
<function: () -> IO ()>
*Main> :set -Wall
*Main> print print
<interactive>:1:6:
Warning: Defaulting the following constraint(s) to type `()'
`Show a' arising from a use of `print' at <interactive>:1:6-10
`Typeable a' arising from a use of `print' at <interactive>:1:0-10
In the first argument of `print', namely `print'
In a stmt of a 'do' expression: it <- print print
<function: () -> IO ()>
*Main> print putStrLn
<function: [Char] -> IO ()>
*Main> print id
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`Typeable a' arising from a use of `print' at <interactive>:1:0-7
Probable fix: add a type signature that fixes these type variable(s)
*Main> print (id :: Int -> Int)
<function: Int -> Int>
*Main> print (undefined :: Int -> Int -> Int)
<function: Int -> Int -> Int>
> (read . show) == id
ale może być użyteczna. Można ją znaleźć w przypadku GHC 6.10 w module Text.Show.Functions. Wygląda ona tak:
instance Show (a -> b) where
showsPrec _ _ = showString "<function>"
Dzisiaj wpadłem na nieco pożyteczniejszą formę. Niestety, wszystko ma swoją cenę - zadziała ona tylko dla funkcji których argumenty są elementami klasy Typeable.
instance (Typeable a, Typeable b) => Show (a -> b) where
show _ = "<function: " ++ show (typeOf (undefined :: a)) ++ " -> " ++ show (typeOf (undefined :: b)) ++ ">"
Przykładowe działanie:
*Main> print print
Loading package syb ... linking ... done.
<function: () -> IO ()>
*Main> :set -Wall
*Main> print print
<interactive>:1:6:
Warning: Defaulting the following constraint(s) to type `()'
`Show a' arising from a use of `print' at <interactive>:1:6-10
`Typeable a' arising from a use of `print' at <interactive>:1:0-10
In the first argument of `print', namely `print'
In a stmt of a 'do' expression: it <- print print
<function: () -> IO ()>
*Main> print putStrLn
<function: [Char] -> IO ()>
*Main> print id
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`Typeable a' arising from a use of `print' at <interactive>:1:0-7
Probable fix: add a type signature that fixes these type variable(s)
*Main> print (id :: Int -> Int)
<function: Int -> Int>
*Main> print (undefined :: Int -> Int -> Int)
<function: Int -> Int -> Int>
środa, lutego 11, 2009
Rozmowa o pracę
Stanowisko: informatyk / programista.
Tak, wiem, że to dość ogólne określenie. Nie mniej jednak ktokolwiek przychodzi na rozmowę kwalifikacyjną i wpisuje w CV znajomość C++ powinien oczekiwać, że poprosi się go o napisanie jakiegoś programu. Prostego. Na przykład liczącego n-ty element ciągu Fibonacciego. Prosty, banalny wręcz program.
Nie oczekuję wersji jedno-dwulinijkowej w Haskellu:
fib n = fibs !! n
fibs = 0 : 1 : (zipWith (+) fibs (tail fibs)) :: [Integer]
Nie oczekuję też rozwiązania powiązanego równania rekurencyjnego i napisania tej funkcji tak, by działała w czasie (praktycznie) stałym (zostawiam to zadanie czytelnikom).
Oczekuję prostej definicji w C. Ba, może być nawet taka, która ma złożoność wykładniczą!
int fib(int n)
{
if (n==0) return 0;
if (n==1) return 1;
return fib(n-1) + fib(n-2);
}
Jak widać powyżej nie ma nawet sprawdzenia czy argument jest ujemny czy też nie. Pisanie powyższych kilku linijek przez pół godziny (i to bez powodzenia...) to strata czasu - obu stron.
Tak, wiem, że to dość ogólne określenie. Nie mniej jednak ktokolwiek przychodzi na rozmowę kwalifikacyjną i wpisuje w CV znajomość C++ powinien oczekiwać, że poprosi się go o napisanie jakiegoś programu. Prostego. Na przykład liczącego n-ty element ciągu Fibonacciego. Prosty, banalny wręcz program.
Nie oczekuję wersji jedno-dwulinijkowej w Haskellu:
fib n = fibs !! n
fibs = 0 : 1 : (zipWith (+) fibs (tail fibs)) :: [Integer]
Nie oczekuję też rozwiązania powiązanego równania rekurencyjnego i napisania tej funkcji tak, by działała w czasie (praktycznie) stałym (zostawiam to zadanie czytelnikom).
Oczekuję prostej definicji w C. Ba, może być nawet taka, która ma złożoność wykładniczą!
int fib(int n)
{
if (n==0) return 0;
if (n==1) return 1;
return fib(n-1) + fib(n-2);
}
Jak widać powyżej nie ma nawet sprawdzenia czy argument jest ujemny czy też nie. Pisanie powyższych kilku linijek przez pół godziny (i to bez powodzenia...) to strata czasu - obu stron.
wtorek, lutego 03, 2009
Przetestuj, jak TWOJA przeglądarka renderuje UTF-8
A tak na prawdę to tylko krótki programik do wypisania wszystkich znaków rozpoznawanych jako litery w kodowaniu UTF-8.
> module Main where
> import Prelude hiding ( writeFile )
> import System.IO.UTF8 ( writeFile )-- package 'utf8-string' on Hackage
> import Data.List.Split ( splitEvery ) -- package 'split' on Hackage
> import Data.Char ( isAlpha )
> import Data.List ( intercalate )
> main = writeFile "out.html" (header ++ chars ++ footer)
> where
> header = "<HTML><HEAD><META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=utf-8\"></HEAD><BODY>"
> chars = intercalate "\n<br>" . splitEvery 40 . filter isAlpha $ [ minBound .. maxBound ]
> footer = "</BODY></HTML>"
Przykładowy wynik działania znajduje się tutaj. Treść tego posta jest jednocześnie kodem programu - wystarczy zaznaczyć całość, skopiować i zapisać z rozszerzeniem .lhs. Zrozumie go GHC, choć do uruchomienia będą potrzebne dodatkowe pakiety (patrz kod programu).
> module Main where
> import Prelude hiding ( writeFile )
> import System.IO.UTF8 ( writeFile )-- package 'utf8-string' on Hackage
> import Data.List.Split ( splitEvery ) -- package 'split' on Hackage
> import Data.Char ( isAlpha )
> import Data.List ( intercalate )
> main = writeFile "out.html" (header ++ chars ++ footer)
> where
> header = "<HTML><HEAD><META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=utf-8\"></HEAD><BODY>"
> chars = intercalate "\n<br>" . splitEvery 40 . filter isAlpha $ [ minBound .. maxBound ]
> footer = "</BODY></HTML>"
Przykładowy wynik działania znajduje się tutaj. Treść tego posta jest jednocześnie kodem programu - wystarczy zaznaczyć całość, skopiować i zapisać z rozszerzeniem .lhs. Zrozumie go GHC, choć do uruchomienia będą potrzebne dodatkowe pakiety (patrz kod programu).
niedziela, lutego 01, 2009
Evil macro magic
Ostatnio w zobaczyłem ciekawy fragment kodu (C++):
#include "settings.hpp"
/* evil macro magic */
#undef _SETTINGS_HPP
#define extern
#include "settings.hpp"
#undef extern
Ciężko mi skomentować tak ciężkie nadużycie - które z drugiej strony jest genialne :-) Wyjaśnienie: powyższe linijki sprawiają, że wszystkie deklaracje "extern" zmiennych w "settings.hpp" stają się zwykłymi deklaracjami. Może to prowadzić do bardzo nieprzyjemnych błędów, ale zapewne czasami jest to użyteczne.
#include "settings.hpp"
/* evil macro magic */
#undef _SETTINGS_HPP
#define extern
#include "settings.hpp"
#undef extern
Ciężko mi skomentować tak ciężkie nadużycie - które z drugiej strony jest genialne :-) Wyjaśnienie: powyższe linijki sprawiają, że wszystkie deklaracje "extern" zmiennych w "settings.hpp" stają się zwykłymi deklaracjami. Może to prowadzić do bardzo nieprzyjemnych błędów, ale zapewne czasami jest to użyteczne.
piątek, stycznia 30, 2009
Ciąg Fibonacciego
Chyba najkrótsza definicja jaką widziałem:
> import Control.Monad.Fix
> fibs :: [Integer]
> fibs = fix ((0:) . scanl (+) 1)
Haskell FTW!
> import Control.Monad.Fix
> fibs :: [Integer]
> fibs = fix ((0:) . scanl (+) 1)
Haskell FTW!
środa, stycznia 07, 2009
Zabawy z Twitterem i GDB
Dzisiaj miałem potrzebę uruchomić serwer gry Killer Mud na debuggerze gdb: wczorajsze zmiany w kodzie spowodowały, że w ciągu doby serwer 3 razy się wywalił. Ponieważ chcę mieć możliwość debugowania kodu w przypadku jakichkolwiek problemów proces zostanie zamrożony. Sesja GDB uruchomiona jest spod screen'a, dzięki czemu nie muszę być zalogowany na serwerze by program mimo wszystko działał.
Tutaj jednak pojawia się problem: skąd mam wiedzieć, że program ciągle działa? Chciałbym dostawać jakieś powiadomienie w razie wystąpienia problemu. Poszperałem w paru źródłach, założyłem konto na Twitterze i ostatecznie stworzyłem taki oto plik .gdbinit:
Jak można się domyśleć, o zatrzymaniu muda wysyłane są powiadomienia na Twittera, podobnie o kontynuacji jego uruchomienia. Przy zatrzymaniu wysyłany jest także mail.
Stworzenie sesji jest dość proste:
Teraz robimy tylko "Ctrl-a d" i mamy działającą w tle sesję GDB.
Garść linków:
Tutaj jednak pojawia się problem: skąd mam wiedzieć, że program ciągle działa? Chciałbym dostawać jakieś powiadomienie w razie wystąpienia problemu. Poszperałem w paru źródłach, założyłem konto na Twitterze i ostatecznie stworzyłem taki oto plik .gdbinit:
define hook-run
set logging overwrite off
set logging file killer-gdb-log.txt
set logging on
end
define hook-stop
backtrace
shell rm -rf hook-stop-log.txt
shell tail -n 50 killer-gdb-log.txt >> hook-stop-log.txt
shell echo "----------------------" >> hook-stop-log.txt
shell tail -n 50 killer-run-log.txt >> hook-stop-log.txt
shell tar -czf killer-gdb-log.txt.tgz killer-gdb-log.txt
shell tar -czf killer-run-log.txt.tgz killer-run-log.txt
shell echo | mail -a killer-gdb-log.txt.tgz -a killer-run-log.txt.tgz -q "hook-stop-log.txt" -s "killer :: gdb :: hook-stop" ADRES@MAILOWY.COM
shell ~/tener/twidge update "killer :: gdb :: hook-stop"
end
define hook-continue
shell ~/tener/twidge update "killer :: gdb :: hook-continue"
end
Jak można się domyśleć, o zatrzymaniu muda wysyłane są powiadomienia na Twittera, podobnie o kontynuacji jego uruchomienia. Przy zatrzymaniu wysyłany jest także mail.
Stworzenie sesji jest dość proste:
screen
cd ~/mud/area
gdb ../src/rom
run 3000 >> killer-run-log.txt 2>&1
Teraz robimy tylko "Ctrl-a d" i mamy działającą w tle sesję GDB.
Garść linków:
- Screen: http://en.wikipedia.org/wiki/GNU_Screen
- GDB, manual: http://www.linuxtopia.org/online_books/redhat_linux_debugging_with_gdb/index.html
- Twitter: http://twitter.com
- Twidge, klient Twittera napisany w Haskellu: http://software.complete.org/software/projects/show/twidge
- moje konto na Twitterze: http://twitter.com/tener
- konto muda na Twitterze: http://twitter.com/killermud
Subskrybuj:
Posty (Atom)