wtorek, 10 marca 2015

Przezroczysty wskaźnik (PIMPL), czyli piękno rzeczy prostych

To jeden z moich ulubionych wzorców projektowych. Przydaje się zarówno do uproszczania nagłówka klasy jak i skraca czas kompilacji w dużych projektach. Przezroczysty wskaźnik (lub po prostu PIMPL) to wzorzec tworzenia abstrakcji pewnej struktury/klasy tak, aby (w najczęstszym przypadku) ukryć całą informację o budowie części prywatnej. Z racji, że jakakolwiek zmiana w części prywatnej dzieje się poza nagłówkiem, taka zmiana nie wymusza rekompilacji wszystkich plików zależnych od tego nagłówka. W dużym projekcie to na prawdę duża oszczędność.

Urok przezroczystego wskaźnka bierze się z zasady kompilacji w C/C++. Jeżeli klasa/funkcja posiada/bierze wskaźnik na typ złożny (T*) to dopóki nie próbujesz wyłuskać składowej klasy T lub utworzyć jej instancji, kompilatora nie interesuje budowa typu T. Dlaczego? Bo wskaźnik ma zawsze ten sam rozmiar, więc kwestia alokacji jest jasna. Aby kompilator nie narzekał, że używasz typu nieokreślonego w nagłowkach wystarczy zrobić deklarację wprzódstruct T. Przy deklaracji wprzód, ponieważ nie zamierzamy odnosić się do budowy tego typu, słowo struct i class można stosować zamiennie.

Koncepcja wskaźnika przezroczystego:

  • W nagłówku, części prywatnej, deklarujesz wprzód, że dana klasa ma lokalną strukturę, ale jej nie opisujesz
  • W nagłówku, części prywatnej, określasz że dana klasa ma składową typu wskaźnik na lokalną strukturę (wciąż nieopisaną)
  • W pliku .cpp dostarczasz budowę klasy TwojaKlasa::StrukturaWewnętrzna
  • W konstruktorze swojej klasy tworzysz instancję klasy lokalnej podpinając ją pod widoczny w nagłówku wskaźnik
W kodzie wygląda to tak: klasa.hpp
#ifndef KLASA_H
#define KLASA_H

#include <string>

class Klasa
{
  public:
    Klasa();
    ~Klasa();

    std::string twojeImie() const;

  private:
    struct Pimpl;
    Pimpl* priv;
};

#endif //KLASA_H

klasa.cpp
#include "klasa.hpp"

struct Klasa::Pimpl
{
  std::string imie;

  Pimpl(const std::string& imie)
  {
    this->imie = imie;
  }
};

Klasa::Klasa()
{
  priv = new Pimpl("Imie");
}

Klasa::~Klasa()
{
  delete priv;
}

std::string Klasa::twojeImie() const
{
  return priv->imie;
}

Jedyny dyskomfort związany z użyciem przezroczystego wskaźnika wiąże się z debuggowaniem kodu, bo mimo iż gdb ma dostęp do składowych chronionych klasy, a tym samym struktury lokalnej, to niekiedy z niewiadomej przyczyny GDB traci zdolność wykonania introspekcji budowy klasy lokalnej, na szczęście nie jest to częste. Jeżeli ktoś z Was wie jak temu zaradzić, będę wdzięczny za notkę w komentarzach.

wtorek, 27 stycznia 2015

C++: Operator przecinka, czyli jak ładnie zrobić pętlę

Dzisiaj w kodzie popełnionym przez kogoś z firmy, odnalazłem perełkę pokazującą inny sposób tworzenia pętli for w oparciu o iteratory. Tym drobiazgiem czyniącym róznicę jest sprytne wykorzystanie operatora przecinka i konstruktora iteratora. Wygląda to tak:

#include <vector>
#include <iostream>
int main()
{
  int array[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
  std::vector<int> vector(array, array+8);

  for ( std::vector<int>::const_iterator it(vector.begin()), end(vector.end());
  end != it;
  ++it )
    std::cout << *it << std::endl;

  return 0;
}

Takie proste, a jakie ładne.

czwartek, 25 grudnia 2014

Dekorator w Python: Testowanie: Podmiana ciała funkcji

Z racji na wygodną składnię i elastyczność Python'a używanie dekoratorów jest potężnym środkiem, szczególnie w pisaniu aplikacji sieciowych, czy testowaniu oprogramowania. Chciałbym pokazać prosty przykład tego, jak wygodnie i elegancko podstawiać ciała funkcji na potrzeby testów.

Ogólna idea dekoratora w Python

Mamy funkcję bezargumentową o nazwie przywitaj_sie, która wypisuje na ekran i zwraca wartość prawdy. Chcąc objąć ją dekoratorem o nazwie dekorator (nazwa zmyślona), należy przy definicji użyć następującej notacji:
@dekorator
def przywitaj_sie():
    print 'Witaj'
    return True
Dekorator zazwyczaj jest funkcją, która ma zwrócić wywołalny obiekt, który podmieni przywitaj_sie. Definiując go nad funkcją powodujesz, że dekorator się wykonuje w chwili udekorowania funkcji przywitaj_sie a następnie zwraca wywoływalny obiekt podmieniający działanie funkcji przywitaj_sie. Dwa wnioski: dekorator (zazwyczaj funkcja) winien przyjmować jeden parametr, który jest wywoływalny; dwa: winien zwracać obiekt wywoływalny o takiej samej ilości parametrów jak funkcja, którą dekoruje (tu: zero).

W wielu przypadkach wręcz wskazane jest użycie domknięć. Definiując dekorator dla funkcji z przykładu powyżej można zrobić tak:

def dekorator(callme):
    def zamiennik():
        return False
    return zamiennik

Po udekorowaniu funkcji każde wywołanie funkcji przywitaj_sie zwraca wyłącznie False.

Dekorator może zwrócić swój argument, czyli teoretycznie nic nie zmieniać. To podejście stosowane jest szczególnie w wypadku mechanizmów rejestrowania wywołań zwrotnych (callbacks), gdyż dekorator jest uruchamiany podczas dekorowania każdej funkcji, a nie przy jej wywołaniu. Programowanie sieciowe notorycznie korzysta z tego mechanizmu np. do ustalania rejestrowania akcji na określony wzorzec URL albo rejestrowania wywołań zwrotnych (callback) na określony kod stanu HTTP.

Wracając do podstawień funkcji... Argumentem dekoratora może być cokolwiek, co jest wywoływalne, np. lambda. Dekorator musi zwracać cokolwiek wywoływalnego, może być również lambda. Jeszcze inna rzecz, że dekorator musi być wywoływalny (callable), co w wypadku Pythona oznacza bycie regularną funkcją, lambdą lub ... klasą ze zdefiniowaną metodą __call__().

Moja koncepcja podmiany wywołań funkcji (przydatne podczas testowania) to:

class Mocker(object):
    def __init__(self, substitute):
        assert(callable(substitute))
        self.substitute = substitute
    def __call__(self, mocked):
        assert(callable(mocked))
        return self.substitute
I przykłady zastosowania:
import random

@Mocker(lambda : random.random() > 0.5)
def zwroc_false():
    return False

for i in range(10):
    print zwroc_false()

@Mocker(lambda x,y : x*y)
def suma(a,b):
    return a+b

print suma(2,3)
print suma(3,4)

@Mocker(lambda s : s+'snake')
def podwoj_lancuch(lancuch):
    return s*2

print podwoj_lancuch('Ala')
print podwoj_lancuch('Alicja')

niedziela, 30 listopada 2014

Problem z operatorem trójelementowym

Parę dni temu C++ robił mi za kawę: podniósł ciśnienie bez łyka kofeiny. Poniższy kod:

#include <iostream>
#include <string>

std::ostream& operator<<(std::ostream& stream, const char* chr)
{
        stream << 0;
        return stream;
}

std::ostream& operator<<(std::ostream& stream, const std::string& str)
{
        stream << 1;
        return stream;
}

int main()
{
        std::cout << ( true ? "CCharStar" : std::string("StdString") ) << std::endl;
        std::cout << ( true ? std::string("StdString") : "CCharStar" ) << std::endl;

        return 0;
}
przeciąża operator strumienia dla łańcucha znaków oraz std::string. Zgodnie z pobieżną logiką pierwszym przypadku użycia operatora trójelementowego, z racji że warunek jest spełniony, winno wypisać "0", a w drugim "1".

To jest C++, więc nie może być łatwo. Niuans pokazany w tym kodzie był pośród setek innych linii, które posądzałem o nieprawidłowe działanie kodu. Po głębszej analizie tego, co powoduje błąd/niezrozumienie sprawa okazuje się całkiem prosta, ale serio mówiąc zajęło to trochę czasu.

W obu przypadkach zostanie wypisane "1", mimo że w pierwszym przypadku oczekiwałoby się 0. Przyczyną jest to, że operator trójpunktowy zawsze oczekuje, aby typ zwracany przez obie sekcje był identyczny, tym samym operator ?: nie jest wymienny z if-else. Tutaj są dwa różne typy, więc kompilator samowolnie szuka metody rzutowania jednego w drugi. Ponieważ std::string da się zbudować z const char*, to dochodzi do tego absurdu. Sytuacji by nie było, gdyby std::string miał konstruktor typu explicit, co oznacza niemożność niejawnego utworzenia zmiennej tego typu (np. poprzez operator trójelementowy).

Nie neguję słuszności tego mechanizmu a jedynie chcę się podzielić spostrzeżeniem, które - w bardziej 'życiowym' kodzie potrafi sprawić niemałą zagwozdkę, szczególnie w przypadku gdy programista nie jest zupełnie świadom, że oba typy użyte w tym operatorze powiązane są hierarchią dziedziczenia lub jeden da się zbudować z drugiego. Ani GCC, ani Clang (w domyślnej konfiguracji) nie wychwytują tego jako błędu. Następnym razem opiszę cyrk z tym operatorem gdy właczymy do tego dziedziczenie.

czwartek, 26 czerwca 2014

[Linux] KDE Rekonq i luka w bezpieczeństwie

Od jakiegoś czasu miałem problem z aktualizacjami systemu, w szczególności duże update'y szły jak krew z nosa. Łącze rzędu 40-50kbps przypominały mi czasy modemów ściągania map do Quake po 2 godziny jedna. Pomimo przepisania sources.list na mirror innego kraju problem nie ustępował. Dzisiaj podczas ściągania pakietu o rozmiarze < 1MB dostałem kolejne odrzucenie. Coś mnie zaintrygowało...

Od pewnego czasu próbnie używam KDE Rekonq - takiej lekkiej przeglądarki opartej o WebKit. Skonfigurowałem ją jakiś czas temu tak, aby działała przez zagraniczne proxy. Rozwiązanie? Ustawiając proxy w Rekonq ustawienie propagowane jest do konfiguracji całego KDE. Niniejszym, używając równolegle Firefox'a czy ręcznego apt-get wszystko będzie w porządku, ale np. aktualizując system za pomocą graficznego KDE Muon wszystkie aktualizacje także idą przez proxy. To samo dotyczy między innymi usług kryptograficznych (pobieranie kluczy GPG dla oprogramowania) czy pobierania paczek "security". Piękna dziura.

Aha.. teraz pobieram aktualizacje ok. 1.8Mbps.

niedziela, 23 marca 2014

Raport o zarobkach w zawodzie programisty w Polsce - Sedlak&Sedlak

Otrzymałem raport na temat wynagrodzeń na stanowisku inżynier oprogramowania w Polsce. Raport został przygotowany przez firmę Sedlak&Sedlak.

Dwie konkluzje:
  • Mityczne zarobki programistów rzędu 15 000 zł netto to bajka
  • Programiści z wyższym zarabiają mniej od tych z podyplomowym
Raport do pobrania stąd: http://pl.scribd.com/doc/213989917/Analiza-593-Inzynier-oprogramowania-pdf

Processing.js - Processing dla JavaScript

Dawno temu, w jednym z pierwszych postów hucznie zapowiedziałem język Processing. Nie zmieniam zdania, że to jeden z najbardziej innowacyjnych pomysłów ostatnich lat, bo jako jedyny umożliwia przyzwoite programowanie osobom, które nie są programistami - co więcej, pomimo że był celowany w nieprogramistów, Processing ma coraz silniejsze grono użytkowników ze strony profesjonalistów. Dlaczego? Bo nikt nie lubi się męczyć, a skoro można coś zrobić w Processing, to po co utrudniać.

Od niedawna istnieje projekt Processing.js, który umożliwia translację kodu Processing do natywnego JavaScript. Podkreślam, że nie ma miejsca żadna kompilacja. Całość pracuje natywnie w JavaScript. Oprócz oczywistej korzyści z odrzucenia konieczności eksportu do apletu Java pojawia się ogromny potencjał integracji Processing z milionem wspaniałych narzędzi: począwszy do D3JS, czy choćby jakiś MVC - w rezultacie tworzenia stron WWW z interfejsem artystycznym. Bajka!

Podtrzymuję, że JavaScript jest najważniejszą technologią tej dekady.

poniedziałek, 17 marca 2014

Google Dev Art

Dopiero dziś się o tym dowiedziałem!
Trwa Google DevArt, czyli konkurs prac wizualnych napędzanych kodem. Projekty mogą pracować pod Androidem, czy Linuxem. Dopuszczono między innymi Javę, HTML5, Cpp czy Python. Można korzystać z ogromu zewnętrznych bibliotek, także OpenCV. Co ciekawe, projekty mogą korzystać z Arduino czy Raspberry PI. Brzmi cudnie ^-^

piątek, 7 lutego 2014

Wprowadzenie do STL

Kiedyś, dawno temu, miałem okazję prowadzić w ramach koła naukowego wprowadzenie do C++ STL. Materiały szczególnie przydadzą się maturzystom wybierającym C++, studentom rozpoczynającym pracę z C++ oraz osobom, które wolą C.

Część pierwsza: http://secred.pl/2012/06/16/stl-materialy-z-prezentacji/
Część druga: http://secred.pl/2012/06/16/stl-podejscie-drugie-materialy-z-prezentacji/

wtorek, 4 lutego 2014

C/C++: Operator nawiasu kwadratowego

Niech:

int tab[] = { 1, 2, 3, 4, 5 };
const int n = 2;


wtedy można zrobić tak:

int wynik = tab[n];

ale ale ...... można też tak:

int wynik = n[tab];

Dziwne?

W zasadzie tak wyszło przez przypadek. Gdy powstawał C (a może nawet B), operator nawiasu kwadratowego był lukrem składniowym. Nim wprowadzono go do języka n-ty element tablicy tab otrzymywano w taki sposób:

T operator[]( loperand, roperand )
{
  return *(loperand+roperand);
}
(co całkiem nieźle tłumaczy czemu tablice numerujemy od zera)

Później wprowadzono operator[], który wewnątrz robi dokładnie to samo, co kod powyżej. Ponieważ relacja jest przemienna, bo *(tab+n) <=> *(n+tab), to możliwe jest zamiana indeksu i tablicy.

Lubię ten język : )

piątek, 31 stycznia 2014

Dziwny błąd, jeszcze dziwniejsze rozwiązanie

Przypadkiem dowiedziałem się, że brak przecinka między dwoma łańcuchami jest poprawnym wyrażeniem C. Co zabawniejsze, oznacza konkatenację.

Weźmy taki przykład:

char* tab[] = { "Ala", "ma", "kota", "imieniem" "Filemon" };


zatem tab[4] powoduje segfault, ale program się całkiem zacnie skompiluje.

Zapominając o przecinku można się tyyyle nauczyć ;)
Edit: W GCC nawet dołączenie -ansi -pedantic -Wall nie powoduje pojawienia się ostrzeżenia.

niedziela, 26 stycznia 2014

C++: Mały błąd, duży problem

Strasznie dawno mnie tu nie było ...

Dziś do szybkiego przemyślenia coś, co potrafi wprawić w osłupienie gdy niewinne parę linijek kodu blokuje działanie dużego modułu, który nie ma nic wspólnego z matematyką.

EDIT Po poście Sebastiana uprościłem kod do absolutnego minimum, aby uwidocznić problem
Oto i on!
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
 
unsigned long long int suma = 0;
 
int main()
{
 srand( time ( NULL ) );

 unsigned long long int losowa = rand() << 10 | rand() << 5 | rand();

 for ( int i = 63; i >= 0; i-- )
 {
  if ( ( (1<<i) & losowa ) > 0  )
  {
   suma += ( 1<<i );
  }
 }
 std::cout << "Suma: " << suma << std::endl;
 std::cout << "Losowa: " << losowa << std::endl;
 return 0;
}

niedziela, 21 lipca 2013

Proste filtry graficzne w Processing : zrozumieć zapis koloru

Strasznie mi wstyd, że od miesiąca nie znalazłem czasu, by tu zajrzeć.
Dziś wrócę do moich korzeni, czyli styku programowania i grafiki komputerowej. Chciałbym pokazać osobom mniej zaawansowanym jak napisać prosty filtr do obrazu statycznego. Będę pracował nad zdjęciem Cateriny, której fotkę można znaleźć tu: http://commons.wikimedia.org/wiki/File:Luca_Patrone_Caterina_in_autumn.jpg Obraz pochodzi z Wikicommons, czyli możemy z niego legalnie i za darmo korzystać; objęty jest licencją CC BY-SA (tutaj możesz poczytać o czym ona mówi). Obraz został znormalizowany i skadrowany.

Filtr, który będę pokazywał nazywa się plamą barwną. Obraz po zadziałaniu filtra winien wyglądać, jakby był malowany farbami. Znajdujące się obok siebie piksele powinny zlać się ze sobą dając efekt plamy, która powstanie poprzez zatarcie drobnych różnic kolorystycznych pomiędzy pikselami. Nie będziemy rozmazywali obrazu.

Zanim jednak, muszę przypomnieć dwie tożsamości z logiki Boole'a. Z tabeli prawdy koniunkcji wynika, że:

W językach C, C++ czy Java (Processing jest nakładką na Java) istnieją dwa operatory koniunkcji. Pojedynczy ampersand (&) oraz podwójny (&&). Użycie podwójnego ampersanda bierze lewe i prawe wyrażenie, i pomiędzy tymi dwoma wyrażeniami oblicza wartość logiczną. Nas bardziej interesuje operator pojedynczego ampersanda. Oba operandy pojedynczego ampersanda (liczby, nie wartości logiczne) są traktowane operatorem koniunkcji w taki sposób, że pierwszy bit pierwszego operandu jest w koniunkcji z pierwszym bitem drugiego operandu, drugi bit pierwszego operandu z drugim bitem drugiego operandu itd.




Na rysunku powyżej pokazałem przykład działania operatora ampersand. Z tożsamości u góry wynika, że te bity drugiego operandu na których są jedynki spowodują przepisanie bitów pierwszego operandu, a tam gdzie drugi operand ma zera, w wyniku znajdą się zera. Aby było szybciej, operację koniunkcji bitowej będę nazywał and-owaniem.

Zakładam, że obraz zapisany jest za pomocą przestrzeni koloru RGB, 24 bity / piksel, w formacie RGBRGBRGB... Załóżmy, że obraz jest w skali szarości, czyli R=G=B. Na ostatnim bicie (prawym, LSB) można zapisać wartość co najwyżej wartość 1, na skrajnie lewym (MSB) co najwyżej 127.

Jesteśmy w skali szarości. Wykonując koniunkcję piksela przez wartość 255 jego wartość się nie zmieni, natomiast jeżeli dwa piksele różnią się minimalnie (o jeden), to gdyby wykonać koniunkcję przez 0b11111110, czyli 254, to różnice między tymi pikselami by się zatarły. Bazując na tej zasadzie będzie wykonywany efekt plamy barwnej. Wpierw na obrazie w skali szarości (znormalizowanym), później kolorowym. Caterina będzie miała ciężki dzień.

Przygotowałem następujący zestaw masek przez które będę and-ował kolejne piksele obrazka:




odpowiadające kolejno wartościom: 0xFF, 0xF8, 0xF0, 0xE0, 0xC0, 0x80. Ponieważ każda z tych masek dotyczy tylko jednej składowej koloru, to w praktyce każdy piksel trzeba będzie and-ować przez 0xFFFFFF, 0xF8F8F8 itd...

Wraz z kolejną maską zacierane będzie coraz więcej informacji o kolorze, co oznacza że coraz liczniejsze grupy pikseli będą miały identyczną wartość.

Uruchomiłem kod generujący maskowanie pikseli zgodnie z opisanym algorytmem. Wyszło tak:


Lewy górny róg obrazu opisuje wartość hex maski użytej do uzyskania obrazu.

Na obrazie kolorowym można spodziewać się efektów dodatkowych. Wartości RGB będą różne, zatem możliwe że dla danego koloru przeand-owanie piksela spowoduje że nie wszystkie składowe koloru ulegną zmianie, a w rezultacie że na obrazie pojawią się kolory, których nigdy nie było. Przykład:


 
Oba kolory po prawej powstały przez przeand-owanie koloru po lewej, lecz tak jak w pierwszym wypadku z zieleni przeszło w zieleń, tak w drugim pojawił się niepodobny kolor. Wracając, na obrazie kolorowym wyszło tak:


Kod generujący powyższe filtr w języku Processing wygląda następująco:


/*
 * Prosty filtr graficzny - efekt barwnej plamy w Processing
 * Copyright (C) 2013  Adam 'foo-script' Rakowski
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.

 * Źrodło obrazka z przykładu: http://commons.wikimedia.org/wiki/File:Luca_Patrone_Caterina_in_autumn.jpg
 * Obraz na licencji CC BY-SA
 */


PImage przed;

void setup()
{
  przed = loadImage("obraz.jpg");
  if (przed==null)
  {
    print("Obraz nie istnieje");
    return;
  }
  
  size( przed.width*2, przed.height*3);
  
  int[] maski = { 0xf8f8f8, 0xf0f0f0, 0xe0e0e0, 0xc0c0c0, 0x808080 };

  image( przed, 0, 0 );
  for (int w=0; w<3; w++)
    for (int k=0; k<2; k++)
    {
      if (w==0 && k==0) continue;
      image( filtr_plamy(przed, maski[2*w+k-1]) , k*przed.width, w*przed.height);
      text( String.format("%H", maski[2*w+k-1]) , k*przed.width, w*przed.height+10 );
    }
}

PImage filtr_plamy(PImage zrodlo, int maska)
{
  assert(zrodlo != null);
  PImage wynik=przed.get();
  wynik.loadPixels();
  for (int i=0; i<zrodlo.width * zrodlo.height; i++)
    wynik.pixels[i] &= maska;
  return wynik;
}

niedziela, 23 czerwca 2013

Przyszłośc komputerów PC

Na Asymco znalazłem bardzo ciekawy artykuł prezentujący zmianę trendu w sprzedaży komputerów ( mobilnych i wolnostojących ). Wykres nr 4 pokazuje trend wśród sprzedaży nowych urządzeń, nie obejmuje urządzeń już dostępnych na rynku. Wniosek jest jeden: cokolwiek produkujesz, celuj wpierw na platformy mobilne.

Artykuł: http://www.asymco.com/2012/01/17/the-rise-and-fall-of-personal-computing/

sobota, 22 czerwca 2013

Dlaczego polskie uczelnie nie są Stanfordem?

Na Coursera ruszył kurs startupów. Całość realizowana jest przez trzech pracowników naukowych Uniwersytetu Stanforda. Fantastyczne, że kurs ma podejście całościowe; z jednej strony filozofia startupów, problematyka zbierania funduszy, kwestie designu, projektu UI i właściwości niefunkcjonalnych, z drugiej podejście techniczne: HTML5 i Node.js, hostingi, ...

Dlaczego nie jesteśmy Stanfordem? Otóż dlatego, że studenci ogromu polskich uczelni nie są uczeni robienia pieniędzy. Co by nie mówić, edukacja ma służyć właśnie temu i nawet jeżeli nikt za nią nie płaci ( "nikt" w systemie socjalnym jest moim ulubionym słówkiem ), to i tak edukacja zdobywana jest po to, aby więcej zarabiać. Uczestniczę w tym kursie i głównym kryterium oceny jest ocena społeczności sieciowej, tj.:
  • przychód z tytułu mikropłatności (BitCoin)
  • popularność usługi na Facebook
  • popularność usługi na Twitter
Przez tyle lat edukacji w Polsce nigdy nikt nie kazał nam wyprodukować usługi / produktu z zastosowaniem komercyjnym, w celu zdobycia wysokiego udziału, wyeliminowania innego produktu bądź po prostu zarobienia maksymalnej kwoty pieniędzy.

Szkoda. Taki cel zmienia sposób działania, planowania i pokazuje zupełnie nowe podejście do procesu wytwarzania oprogramowania. Przykład: pierwszy rok studiów drugiego stopnia, przedmiot: technologie internetowe. Zadanie: zaprojektować dowolną stronę w czystym html + php + mysql. Założenia:
  • tematyka dowolna
  • żadnych frameworków
  • bez jQuery, Dojo, Backbone, CoffeScript, ...
  • PHP czysty, bez korzystania z bibliotek zewnętrznych
  • HTML5? Niewymagany...
  • Walory użyteczności, innowacyjności: nieoceniane
  • Brak możliwości użycia silnika innego na przykład Ruby / Python / JS
  • Bez wykorzystania narzędzi wersjonowania, trac'u, etc etc etc
  • Ocena przez pryzmat popularności i zarobków? Nie-eee
Po co? A gdybym ja to wiedział.
Szkoda, że nie jesteśmy Stanfordem.

środa, 5 czerwca 2013

Programowanie wizualne : Google Blockly

Wczoraj o analizie algorytmów w Python, dziś nieco o programowaniu wizualnym. Ostatnio miałem okazję, by zderzyć się ze współczesnymi językami wizualnymi stworzonymi dla potrzeb nauczania podstaw programowania. Wyszło średnio, ponieważ większość języków będących zwieńczeniem publikacji / monografii na ten temat albo przestało być rozwijanych, albo dotyczą projektów zamkniętych (np. na potrzeby amerykańskiego wojska). Nie znalazłem niczego, co by łączyło wieloparadygmatowość, licencję WiOO, intuicyjny interface i wygodę analizy działania, debuggowania etc.. Na szczęście po drodze wpadło mi w ręce parę na prawdę fajnych narzędzi. Jedno to UUhistle opisane post wcześniej, kolejne to Blockly ( http://code.google.com/p/blockly/ ). Narzędzie zostało stworzone przez Google jako zestaw komponentów do wizualnego wyrażania programów. Celowo unikam zdania "wizualny język programowania", gdyż Blockly samo w sobie to komponenty. Dopiero z nich utworzono przykładową aplikację Blockly Code ( http://blockly-demo.appspot.com/static/apps/code/en.html ) umożliwiającą programowanie wizualne. Aplikacja jest na licencji WiOO, komponenty też, całość działa w przeglądarce i jest napisana w HTML5. Co jeszcze ciekawsze, interfejs jest dużo lżejszy i wygodniejszy, niż w osławionym MIT Scratchu, a na dodatek kod wyklikany z klocków jest na bieżąco zapisywany także w postaci kodu Python / JavaScript / XML.

Blockly prezentuje się następująco:


Oprócz przedstawionego programu "Code", twórcy przygotowali jeszcze kilka innych aplikacji demonstrujących możliwości, chociażby grafika żółwia oparta o ... mapy Google. Tak, tak, za pomocą instrukcji żółwia prowadzimy żółtego ludzika (tego ze StreetView) po mapie, aby doszedł do celu. Zupełnie jak podczas Juwenaliów ;)

Dla powyższego listingu kod Python wygląda tak:


Dość siermiężnie, natomiast w kontekście nauczania podstaw programowania, myślę że doskonale jest łączyć Blockly i UUhistle. Jedno tworzy kod Python, drugie umożliwia wizualizację. Fajny pakiet do nauczania osób, które nigdy nie miały styczności z programowaniem.



Ponieważ znowu doszły mnie słuchy, że "nigdzie nie wytłumaczono dobrze jak działają wskaźniki", wpadłem na pomysł napisania 3-4 wpisów tłumaczących od zera ideę wskaźników. A nuż komuś to pomoże.

wtorek, 4 czerwca 2013

UUhistle - wizualizacja kodu Python

Moje doświadczenie z nauczaniem programowania pokazało, że uczącym się najtrudniej zrozumieć ideę sekwencyjności (mimo że na logikę zmiana kolejności instrukcji winna mieć znaczenie) oraz pojęcia zagnieżdżenia wywołania funkcji i rekurencji.

Niedawno odkryłem narzędzie UUhistle napisane przez Juhę Sorvę z Universytetu w Aalto, w Finlandii. Aplikacja powstała jako element pracy doktorskiej.

 
Aplikacja umożliwia wizualizację sposobu działania kodu napisanego w Python 2.X, w szczególności wizualizację stanu sterty (dostępne zmienne, ich wartości, do czego odnoszą się referencje), stosu (przy rekurencji) oraz prezentowania sposobu w jaki wykonuje się program. W obszarze "A" znajduje się kod (można wklejać), w obszarze "B" całość jest wizualizowana, natomiast "C" to kontrola wykonania programu. Aplikację można uruchamiać w trybie pojedynczej instrukcji, normalnym, a także ... cofać instrukcje. Z tej przyczyny wyłączono możliwość pracy na strumieniach danych, czyli nie można korzystać z plików, gniazd czy potoków. Oprócz tego wyłączono kilka innych możliwości języka. Najważniejsze braki dotyczą: leniwego wartościowania (yield, generatory), rozwinięcia list, dziedziczenie, rozmiaru biblioteki standardowej, możliwości stosowania lambd. Co ciekawe, klasy jako takie są obsługiwane w ograniczonym zakresie. Wszystkie ograniczenia opisano tutaj.

UUhistle wydaje się być narzędziem stworzonym wyłącznie do wizualizacji najprostszych algorytmów. Idealnie wizualizuje np. dlaczego przy zamianie wartości dwóch zmiennych musi istnieć zmienna pomocnicza. Algorytmy tej klasy trudności mogą być bez trudności realizowane za pomocą UUhistle, natomiast prawdę mówiąc jeżeli ktoś potrzebuje wyrażeń generatorowych i rozumie sens leniwego wartościowania, to prawdopodobnie nie potrzebuje UUhistle w jego podstawowym zastosowaniu.

Aplikacja zajmuje ok. 13MB i nie wymaga instalacji Python.

Przykładowy zrzut z wizualizacji sortowania bąbelkowego:



Zalety:
  • świetna wizualizacja (animacje, kolorowanie, wyodrębnianie struktur kodu) 
  • wizualizuje kod Python, a Python jest dobry w nauczaniu programowania
  • dystrybuowane jako JAR, przenośne + żadnych instalatorów
  • tryb "tutorial", tryb interaktywny
  • darmowy do celów niekomercyjnych

Wady:
  • zamknięta licencja, zamknięty kod (aczkolwiek jak się go otworzy w programie dekompresującym... )
  • okno kodu nie koloruje kodu, nie podpowiada składni
  • nie można zmienić ani zwiększyć czcionki ( Courier 9pt )
Moja prywatna opinia: poza bardzo niedopracowanym oknem kodu, cała aplikacja jest świetnie zrobiona. Bardzo polecam.

środa, 10 kwietnia 2013

FLOSSowa wiosna

Cały czas trwają warsztaty programistyczne organizowane przez SzLUUG. Materiały z dotychczasowych prelekcji dostępne są na stronie. Mimo, że studenci (do których adresowano szkolenie) niezbyt dopisali frekwencją, to pewne że odbędzie się kolejna sesja warsztatowa. Ponownie poruszymy temat Git'a oraz prawdopodobnie rozpoczniemy cykl zajęć wprowadzających do Linux. Podczas szkolenia wyszło, że uczestnicy mają problem z elementarnym rozumieniem działania konsoli systemu. Polecenia echo, czy tworzenie pliku poprzez przekierowanie strumienia do nieistniejącego pliku przerosły niektórych. Chcemy to poprawić. Siła Windowsa nie polega na tym, że jest "łatwy". Windows nie jest łatwy, lecz maskując przez użytkownikiem ogrom możliwości jakie powinien dawać system operacyjny sprawia wrażenie prostego. To dziwne, ale mamy 2013 rok a standardowa konsola Windows dalej nie obsługuje wyrażeń regularnych.

Wracając, chcemy pokazać co powinien dawać użytkownikowi dobry system operacyjny i pokazać co może dać użytkownikowi Linux (bez wywyższania go spośród innych systemów). Chcemy pokazać podstawy poruszania się po systemie, wyjaśnić elementy struktury plikowo-dyskowej i wprowadzić do konsoli, a także podstawowych narzędzi (sed/awk/grep/echo/cat, ...). Może uda mi się wprowadzić po cichu kurs dla nauczycieli pokazujący nauczanie wspomagane komputerowo. Chętnie bym pokazał jak pracować z wykorzystaniem narzędzi z KDE-Edu (np. KStep) , narzędzi Tux4Kids, a także jak ciekawie uczyć nauk z pogranicza matematyki, robotyki i programowania (np. z użyciem Robocode).

Oby się udało ^^


Pomimo małej frekwencji na SzLUUGowych warsztatach z C udało się utworzyć fajny plug-in do programu TuxPaint. Autorem jest Łukasz Dmitrowski, który to został wciągnięty w projekt TuxPaint właśnie dzięki warsztatom. Plugin nazywa się XOR, działa w trybie pędzla (w przeciwieństwie do filtrów typu pełnoekranowego). Efektem jego działania jest utworzenie pod pędzlem mozaiki kolorystycznej. Tło pod pędzlem zostaje zamalowane. Kod znajduje się na GitHubie. Dobra robota, Łukasz :)




Opublikowano listy projektów zaakceptowanych w tegorocznym Google's Summer of Code. Życie jest piękne ^^

środa, 3 kwietnia 2013

Processing : gra w życie

Napisałem wizualizację gry w życie Conway'a w Processing.

Kod wygląda tak:
int ileKomorek=40;
int wysKomorki = 8;
int ileStartowych=300;
boolean[][] przed, po;

int ileSasiadow(int i, int j, int rozmiar)
{
  int ile=0;
  int[] xoff = {
    i-1, i, i+1
  };
  int[] yoff = {
    j-1, j, j+1
  };

  for (int ii=0; ii<3; ii++)
  {
    if (xoff[ii] < 0) xoff[ii]+=rozmiar;
    if (yoff[ii] < 0) yoff[ii]+=rozmiar;
    if (xoff[ii] >= rozmiar) xoff[ii]-=rozmiar;
    if (yoff[ii] >= rozmiar) yoff[ii]-=rozmiar;
  }

  for (int x=0; x<3; x++)
    for (int y=0; y<3; y++)
      if (x!=1 || y!=1)
        if (przed[xoff[x]][yoff[y]]) ile++;

  assert(ile>=0 && ile<=8);
  return ile;
}

void setup()
{
  size(ileKomorek*wysKomorki, ileKomorek*wysKomorki);
  background(255);
  przed = new boolean[ileKomorek][ileKomorek];
  po = new boolean[ileKomorek][ileKomorek];

  int i=0;
  while (i<ileStartowych)
  {
    if (!przed[int(random(0, ileKomorek))][int(random(0, ileKomorek))])
    {
      przed[int(random(0, ileKomorek))][int(random(0, ileKomorek))] = true;
      i++;
    }
  }
  fill(204, 102, 0);
  noStroke();
}

void draw()
{
  background(255);
  for (int i=0; i<ileKomorek; i++)
  {
    for (int j=0; j<ileKomorek; j++)
    {
      if (przed[i][j])
      {
        rect(i*wysKomorki, j*wysKomorki, wysKomorki, wysKomorki);
        po[i][j] = (ileSasiadow(i, j, ileKomorek) >=2 && ileSasiadow(i, j, ileKomorek) <= 3);
      }
      else if (ileSasiadow(i, j, ileKomorek) ==3) po[i][j] = true;
    }
  }

  for (int i=0; i<ileKomorek; i++)
    for (int j=0; j<ileKomorek; j++)
      przed[i][j]=po[i][j];
  delay(1000);
}
A teraz wyobraź sobie, że masz to samo napisać w C++ lub czymś jeszcze bardziej niskopoziomowym.
BTW: Przymierzam się do zrobienia implementacji w Haskell

sobota, 23 marca 2013

Edytor map w MS Excel / LibreOffice Calc

Pare dni temu widziałem prostą gierkę pisaną przez początkującego programistę. Gra polega na chodzeniu ludzikiem (znak ASCII) po mapie rysowanej kodem ASCII. Nie ma szału, ale to doskonały projekt aby wykazać że dobra organizacja kodu ratuje (grzebie) projekt.

Osoba pisząca tą grę wymyśliła sobie problem rysowania mapy w ten sposób, że każda mapa była rysowana oddzielną procedurą. Okey... ale skąd brano topologie mapy? No jasne, że z palca! Każda linia mapy była pisana z palca, znak w znak. W zasadzie procedury rysujące mapy różniły się jedynie sekwencją znaków, a nie samym działaniem. Koszmar :/

Podrzuciłem pomysł rysowania mapy w Excelu. Tak, tak.. w tym arkuszu kalkulacyjnym. Tutaj pokaże to samo używają OpenOffice / LibreOffice Calc, aczkolwiek w Excel też zadziała.

Moja gra będzie oparta na scenariuszu chodzenia po mapie przypominającej labirytnt. Zakładam, że ściany będę oznaczał symbolem "x", podczas gdy punkt startowy gracza oznaczę "@", a punkt docelowy przez "&". Okey, a co ma do tego Excel?

Zaznaczam wszystkie wiersze i kolumny ( przycisk nad wierszem 1, na lewo od kolumny A ), ustalam szerokość kolumny na 0,8 ( zlokalizowane arkusze kalkulacyjne używają przecinka jako separatora części ułamkowej ).


Teraz cały arkusz wygląda mniej-więcej, jakby był złożony z kwadratów. Chciałbym, aby po wstawieniu w komórkę znaku x ta komórka się zaczerniła. Dzięki temu będę lepiej widział jak wygląda rysowana mapa. Aby nie kolorować ręcznie, użyję formatowania warunkowego.
Ustalam 3 reguły:
  • czarne tło dla znaku x
  • zielone tło dla znaku startu @
  • czerwone dla końca &


[ LOffice Calc sprytnie zauważy, że czarny tekst na czarnym tle jest średnio widoczny, więc automatycznie wypełnione na czarno komórki będą miały kolor czcionki zmieniony na biel ]

Mapa wyświetlana na terminalu Windows ma domyślnie 80x25 znaków, czyli plansza w Excel zaczyna się na komórce (A,1) a kończy na (CB, 25). Postawiłem ramkę wokół tego obszaru + zmieniłem domyślne tło na niebieski. Dzięki temu nie przedobrzę z obszarem rysowania.

Tak oto za pomocą stawiania x-ów, jednego startu i jednego końca namalowałem mapę. Tutaj jest ona dużo węższa, niż szerokość terminala, ale to w niczym nie przeszkadza.


Ważne, że to co widać w Excel będzie wiernie odwzorowane na ekranie gracza. Tak zrobioną mapę zapisuję do formatu CSV ( comma separate values ). Jako separator ustawiam średnik ( ; ). Urok formatu CSV polega na tym, że w wypadku danych numerycznych obsługuje go każdy cywilizowany program obliczeniowy - począwszy od R przez Matlaby, MathCADy, Mathematica, Octave, NumPy a kończywszy na Excel i Calc. Dodatkowo ten format jest idealny do parsowania. Prościej się nie da.

Po obejrzeniu tego pliku w dowolnym edytorze tekstowym powinien on wyglądać mniej więcej tak:
Puste komórki zostały pominięte, czyli zapis ;; oznacza, że dana komórka nie miała wartości. Obraz może wydawać się "rozjechany", ale bezproblemowo można go poskładać, tj. usunąć znak separatora, a puste pola zamienić na spację. Terminale używają czcionek monospace, dzięki czemu całość pięknie wyrówna się na ekranie.

Na szybko napisałem funkcję, która realizuje wyświetlenie takiego pliku mapy w formacie CSV na konsoli. Kod:

#include <iostream>
#include <fstream>
using namespace std;

inline void parseLine(string s)
{
 char c;
 char delim=';', fill=' ';
 size_t pos=-1;
 
 if (s.empty()) return;
 
 do {
  pos = s.find(delim, pos+1);
  if ( pos != string::npos )
  {
   if ( pos==0 || s[pos-1]==delim) cout << fill;
   else cout << s[pos-1];
  }
 } while (pos != string::npos);
 
 cout << ( c=s[s.size()-1],c == delim ? fill : c ) << endl;
}

int main()
{
 ifstream mapa("mapa.csv");
 string s;
 

 if (!mapa)
 {
  perror("Blad ladowania mapy");
  return -1;
 }

 while ( !mapa.eof() && mapa )
 {  
  getline(mapa, s);
  parseLine(s);
 }

 mapa.close();
 return 0;
}

W rezultacie całość wygląda w konsoli tak:


Przy tak prostych aplikacjach wysilanie się na własny edytor map jest przerostem formy nad treścią. Bardzo zachęcam do korzystania np. z Calc/Excela w takich sytuacjach.