eGospodarka.pl
eGospodarka.pl poleca

eGospodarka.plGrupypl.comp.programmingJaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
  • X-Received: by 2002:a37:9547:: with SMTP id x68mr751652qkd.2.1546591215232; Fri, 04
    Jan 2019 00:40:15 -0800 (PST)
    X-Received: by 2002:a37:9547:: with SMTP id x68mr751652qkd.2.1546591215232; Fri, 04
    Jan 2019 00:40:15 -0800 (PST)
    Path: news-archive.icm.edu.pl!news.icm.edu.pl!news.nask.pl!news.nask.org.pl!news.unit
    0.net!feeder1.cambriumusenet.nl!feed.tweak.nl!209.85.160.216.MISMATCH!v55no9279
    72qtk.0!news-out.google.com!h3ni13741qtk.1!nntp.google.com!v55no927960qtk.0!pos
    tnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail
    Newsgroups: pl.comp.programming
    Date: Fri, 4 Jan 2019 00:40:14 -0800 (PST)
    In-Reply-To: <0...@g...com>
    Complaints-To: g...@g...com
    Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=46.186.77.192;
    posting-account=f7iIKQoAAAAkDKpUafc-4IXhmRAzdB5r
    NNTP-Posting-Host: 46.186.77.192
    References: <c...@g...com>
    <f...@g...com>
    <a...@g...com>
    <7...@g...com>
    <a...@g...com>
    <6...@g...com>
    <0...@g...com>
    <a...@g...com>
    <1...@g...com>
    <e...@g...com>
    <6...@g...com>
    <1...@g...com>
    <2...@g...com>
    <5...@g...com>
    <9...@g...com>
    <q0lcv9$abq$1@gioia.aioe.org>
    <1...@g...com>
    <4...@g...com>
    <1...@g...com>
    <0...@g...com>
    User-Agent: G2/1.0
    MIME-Version: 1.0
    Message-ID: <3...@g...com>
    Subject: Re: Jaki język polecić początkującemu? - komentarz do artykułu w
    Programista 9/2018
    From: g...@g...com
    Injection-Date: Fri, 04 Jan 2019 08:40:15 +0000
    Content-Type: text/plain; charset="UTF-8"
    Content-Transfer-Encoding: quoted-printable
    Xref: news-archive.icm.edu.pl pl.comp.programming:213160
    [ ukryj nagłówki ]

    W dniu piątek, 4 stycznia 2019 01:13:33 UTC+1 użytkownik fir napisał:
    > > Ogólnie derywację tego programu mam dość dogłębnie opisaną
    > > w pierwszym rozdziale "Pamphletu":
    > > https://github.com/panicz/pamphlet/raw/master/pamphl
    et.pdf
    > >
    > > W praktyce zapisałbym go raczej np. tak (jeżeli język
    > > wspierałby leniwą ewaluację):
    > >
    > > (sum (map square (initial 7 (only prime? numbers))))
    > >
    > > gdzie "map", "only" i "initial" są zdefiniowane tak:
    > >
    >
    > jakos nie wyglada mi to na prostsze niz wersja w c

    To nie jest tylko kwestia "wyglądania".
    Do zrozumienia tego kodu wystarczy stosować rozumowanie
    "podstawieniowe", które wcześniej pokazałem.
    Do zrozumienia Twojego kodu w C oprócz podstawienia (które też
    jest potrzebne, bo zdefiniowałeś funkcję z parametrem) trzeba
    też rozumieć takie rzeczy, jak przepływ sterowania albo
    operator przypisania.

    > a co to jest tam wyzej sum square (inty?) prime? (funkcja?) i numbers (???)

    jak mamy wyrażenie
    (sum (map square (initial 7 (only prime? numbers))))

    to sygnatury typów będą następujące:

    sum :: Num a => [a] -> a

    oznacza to, że funkcja "sum" bierze listę elementów o jakimś typie
    numerycznym (czyli takim, dla którego zdefiniowany jest operator dodawania)
    i zwraca element numeryczny. Operator :: czytamy jako "jest typu".
    Rzeczy po lewej stronie operatora "=>" to tzw. "ograniczenia typów",
    zaś to po prawej jest już konkretną sygnaturą. [a] oznacza listę elementów
    typu a.

    map :: (a -> b) -> [a] -> [b]

    map jest funkcją, która pobiera jakąś funkcję jednoargumentową
    oraz listę elementów, do których można zastosować tę funkcję,
    i zwraca listę elementów typu wynikowego funkcji.

    Warto zwrócić uwagę, że choć nasza percepcja jest taka, że "map" jest
    funkcją dwuargumentową, z sygnatury typów możemy wyczytać, że jest to
    funkcja pobierająca jakiś argument (funkcję) i zwracająca funkcję
    pobierającą jakiś argument (listę), czyli - mówiąc w pewnym uproszczeniu
    wszystkie wieloargumentowe funkcje w Haskellu są tak naprawdę jednoargumentowymi
    funkcjami zwracającymi funkcje (ten ficzer nosi nazwę "Currying",
    albo - jak chciała polska tłumaczka książki "Język C++" Stroustrupa
    - "doprawianie" (!))

    Funkcji square nie definiowaliśmy, ale jej definicja będzie taka:

    square x = x * x

    a sygnatura typów to oczywiście

    square :: Num a => a -> a

    (czyli funkcja, która bierze coś numerycznego i zwraca coś numerycznego
    tego samego typu).

    initial :: Int -> [a] -> [a]

    Funkcja only pobiera predykat (czyli funkcję zwracającą wartość Boolowską)
    i listę elementów, do których ten predykat daje się zastosować, i zwraca
    "pomniejszoną" listę elementów:

    only :: (a -> Bool) -> [a] -> [a]

    Warto zwrócić uwagę, że system typów Haskella nie pozwala wyrazić tego,
    że wynik funkcji jest "pomniejszony" (ale są języki, takie jak Idris
    czy Liquid Haskell, w których tego rodzaju własności daje się wyrazić
    w sygnaturze typów)

    Wreszcie, numbers to po prostu lista liczb całkowitych:

    numbers :: [Int]

    > nie wyglada to na prostsze od c (dlugie jakies)

    Funkcje "only", "map" i "initial" są już predefiniowane.
    (tylko "only" nosi tak naprawdę nazwę "filter", a "initial" -- "take").
    Ty w C użyłeś np. operatora "for" bez podawania jego definicji
    (czego w C zresztą i tak nie dałoby się zrobić).

    Gdyby korzystać z gotowych funkcji, i nie chcieć definiować np.
    square, to (zakładając dostępność funkcji isPrime) cały program
    wyglądałby tak:

    foldr (+) 0 (map (^2) (take 7 (filter isPrime [2 ..])))

    Warto przy tej okazji powiedzieć nieco o rodzinie funkcji fold.
    Zasadnicza idea jest taka, że

    fold (*) [a,b,c,d] = a * b * c * d

    Jednak powyższe sformułowanie jest niejednoznaczne, bo prawą
    stronę można interpretować jako

    (a * (b * (c * d)))

    albo jako

    (((a * b) * c) * d)

    albo jako

    ((a * b) * (c * d))

    W praktyce Haskell daje dwie funkcje, foldl i foldr, które
    działają w taki sposób:

    foldl (*) e [a,b,c,d] = (((e * a) * b) * c) * d
    foldr (*) e [a,b,c,d] = a * (b * (c * (d * e)))

    Dodatkowy parametr e sprawia, że funkcje mają sensowną wartość
    również dla listy pustej, i rozluźnia nieco wymagania narzucane
    na operator (*).

    (Oczywiście, (*) niekoniecznie oznacza tutaj mnożenie, ale
    dowolny operator dwuargumentowy)

    > a w to ze to bedzie tak szybkie tez nie do konca wierze

    Informatyka to nie jest religia. Jak w coś nie wierzysz, to możesz
    sprawdzić. (Tzn. ściśle rzecz biorąc pewnie nie będzie "tak szybkie",
    bo Haskellowy runtime ma jakieś tam dodatkowe narzuty)

    > przydaloby sie jednak by bylo choc prostsze a nie wyglada

    Moim zdaniem jest tak proste, jak tylko się da.

    > sam ten kod w c chyab w sumie mozna napisac prsciej
    >
    >
    > int PoliczSumeParuPoczatkowychLiczbPierwszych(int ilu)
    > {
    > int i = 0, dodano = 0, suma = 0;
    >
    > for(;;)
    > if(jest_liczba_pierwsza(++i))
    > {
    > suma += i*i ;
    > if(++dodano==ilu) return suma;
    > }
    > }

    Dla mnie nie jest to ani trochę prostsze od poprzedniej wersji.

Podziel się

Poleć ten post znajomemu poleć

Wydrukuj ten post drukuj


Następne wpisy z tego wątku

Najnowsze wątki z tej grupy


Najnowsze wątki

Szukaj w grupach

Eksperci egospodarka.pl

1 1 1

Wpisz nazwę miasta, dla którego chcesz znaleźć jednostkę ZUS.

Wzory dokumentów

Bezpłatne wzory dokumentów i formularzy.
Wyszukaj i pobierz za darmo: