-
61. Data: 2019-01-04 09:20:16
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: Maciej Sobczak <s...@g...com>
> A teraz inny przykład:
>
> int g(int x) {
> return x + 2;
> }
>
> Jaką wartość ma wyrażenie g(5)?
> Na to pytanie odpowiesz bez problemu.
Tak.
> Nie będziesz musiał zadawać dodatkowego pytania
> w rodzaju "jaka była poprzednia wartość tego wyrażenia?".
Ale to jest bardzo dobre pytanie w przypadku maszyn stanów, ogólnie. Żeby wiedzieć,
jaki będzie następny stan, trzeba wiedzieć, jaki był poprzedni. Właściwie jest to
pytanie, którego nie da się uniknąć. Albo unikanie tego pytania będzie prowadziło do
niepotrzebnych kosztów i komplikacji systemu.
Świat rzeczywisty bardzo dobrze modeluje się maszynami stanów.
> I to właśnie brak tego rodzaju pytań sprawia, że
> ten model jest prostszy.
Bo problem jest prostszy.
Ale weźmy inny przykład - dowolną implementację funkcji skrótu, np. CRC albo inne
MD5. Choćby takie coś:
https://codereview.stackexchange.com/questions/16387
2/md5-implementation-in-c11
Nie mam pojęcia, czy to jest poprawne ani ile złych nawyków tam jest. Chodzi o coś
innego - o wykorzystanie operacji przypisania jako naturalnego mechanizmu dochodzenia
w wielu etapach (być może iteracjach) do właściwej wartości końcowej. To jest inny
model, niż Twoje wyrażenia gotowe do podstawień, ale jest on częsty w realnych
problemach. Tam w środku po prostu jest maszyna stanów. To jest przykład funkcji,
która jako całość nie korzysta ze stanu globalnego ani statycznego i nie trzeba tam
pytać, jaka była poprzednia wartość. Używając nomenklatury języków funkcjonalnych,
funkcja MD5 jest "czystą funkcją". Czyli taka funkcja spełnia Twoje kryterium
prostszego modelu.
Ale jakoś implementacja takiej funkcji z użyciem operatorów przypisania jest bardziej
naturalna, niż bez nich. Tak bardzo, że implementacji bez operatorów przypisania w
ogóle nigdzie nie znalazłem (nie żebym jakoś mocno szukał[*]).
Czy w celu podparcia swoich dotychczasowych tez podjąłbyś wyzwanie napisania takiej
funkcji bez przypisań?
> Używanie statycznych zmiennych w obrębie funkcji nie jest
> niszowe. W niektórych sytuacjach jest użyteczne.
Więc dlaczego taką możliwość krytykujesz? Czy nie lepiej jest taką możliwość mieć (i
nie korzystać gdy nie jest potrzebna), niż nie mieć?
> > Czy to znaczy, że taki program jest równie dobry, jak program, w którym przypisań
nie ma?
>
> Nie rozumiem pytania.
Krytykujesz operacje przypisania jako zły nawyk albo konstrukcję niepotrzebną i
nadmiarową. Tak przynajmniej rozumiem ten (pod)wątek. Natomiast ja się z taką tezą
nie zgadzam i próbuję wykazać, że model programowania z przypisaniami (ogólnie:
imperatywny) jest nie tylko wartościowy, ale często po prostu optymalny i nie tylko
dlatego, że odzwierciedla działanie komputera, ale dlatego, że właściwie
odzwierciedla postawiony problem. Dlatego oczekuję od języka programowania, że będzie
to wspierał jako podstawową operację i konsekwentnie odrzucam języki, które tego nie
mają, jako niepraktyczne.
[*] No dobra, znalazłem:
http://hackage.haskell.org/package/cryptohash-md5
"The implementation is made in C with a haskell FFI wrapper that hides the C
implementation."
No tak. Tak było chyba *praktyczniej*, prawda?
Ale jest też wersja dla "purystów":
http://hackage.haskell.org/package/pureMD5-2.1.3/src
/Data/Digest/Pure/MD5.hs
Totalna masakra. Nie chcę też nawet pytać, co tam robią słowa "unsafe" i czy z takimi
słowami to na pewno nadal jest "pure".
W skrócie: uciekanie od imperatywności jest sztuczne i szkodzi zdrowiu.
--
Maciej Sobczak * http://www.inspirel.com
-
62. Data: 2019-01-04 09:40:14
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: g...@g...com
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.
-
63. Data: 2019-01-04 10:25:33
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: AK <n...@n...net>
On 2019-01-04 02:00, AK wrote:
> W Pythonie wyglada ona np tak (pisane "z palca" o 15 min:):
>
> from itertools import count, islice
Autopoprawka polepszajaca:
from math import sqrt
[...]
erasto = int(sqrt(liczba) + 0.5)
czy_pierwsza = all(liczba % pierwsza for pierwsza in pierwsze
if pierwsza <= erasto)
AK
-
64. Data: 2019-01-04 11:15:29
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: g...@g...com
W dniu piątek, 4 stycznia 2019 09:20:17 UTC+1 użytkownik Maciej Sobczak napisał:
> > A teraz inny przykład:
> >
> > int g(int x) {
> > return x + 2;
> > }
> >
> > Jaką wartość ma wyrażenie g(5)?
> > Na to pytanie odpowiesz bez problemu.
>
> Tak.
>
> > Nie będziesz musiał zadawać dodatkowego pytania
> > w rodzaju "jaka była poprzednia wartość tego wyrażenia?".
>
> Ale to jest bardzo dobre pytanie w przypadku maszyn stanów, ogólnie. Żeby wiedzieć,
jaki będzie następny stan, trzeba wiedzieć, jaki był poprzedni. Właściwie jest to
pytanie, którego nie da się uniknąć. Albo unikanie tego pytania będzie prowadziło do
niepotrzebnych kosztów i komplikacji systemu.
> Świat rzeczywisty bardzo dobrze modeluje się maszynami stanów.
Maszyny stanów bardzo dobrze modeluje się czystymi funkcjami.
> > I to właśnie brak tego rodzaju pytań sprawia, że
> > ten model jest prostszy.
>
> Bo problem jest prostszy.
Tak. I ja właśnie o tym mówię: jeżeli mamy prostsze problemy,
to korzystanie ze środków, które są potrzebne do radzenia sobie
z bardziej złożonymi problemami jest błędem.
(Kiedyś słyszałem określenie "principle of least power")
> Ale weźmy inny przykład - dowolną implementację funkcji skrótu, np. CRC albo inne
MD5. Choćby takie coś:
>
> https://codereview.stackexchange.com/questions/16387
2/md5-implementation-in-c11
>
> Nie mam pojęcia, czy to jest poprawne ani ile złych nawyków tam jest. Chodzi o coś
innego - o wykorzystanie operacji przypisania jako naturalnego mechanizmu dochodzenia
w wielu etapach (być może iteracjach) do właściwej wartości końcowej. To jest inny
model, niż Twoje wyrażenia gotowe do podstawień, ale jest on częsty w realnych
problemach. Tam w środku po prostu jest maszyna stanów. To jest przykład funkcji,
która jako całość nie korzysta ze stanu globalnego ani statycznego i nie trzeba tam
pytać, jaka była poprzednia wartość. Używając nomenklatury języków funkcjonalnych,
funkcja MD5 jest "czystą funkcją". Czyli taka funkcja spełnia Twoje kryterium
prostszego modelu.
>
> Ale jakoś implementacja takiej funkcji z użyciem operatorów przypisania jest
bardziej naturalna, niż bez nich. Tak bardzo, że implementacji bez operatorów
przypisania w ogóle nigdzie nie znalazłem (nie żebym jakoś mocno szukał[*]).
> Czy w celu podparcia swoich dotychczasowych tez podjąłbyś wyzwanie napisania takiej
funkcji bez przypisań?
Mógłbym się podjąć, gdyby problem był dla mnie interesujący.
Akurat liczenie MD5 w obecnej chwili nie jest takim problemem.
> > Używanie statycznych zmiennych w obrębie funkcji nie jest
> > niszowe. W niektórych sytuacjach jest użyteczne.
>
> Więc dlaczego taką możliwość krytykujesz? Czy nie lepiej jest taką możliwość mieć
(i nie korzystać gdy nie jest potrzebna), niż nie mieć?
Nie krytykuję tej możliwości.
Krytykuję korzystanie z tej możliwości wtedy, kiedy nie jest to
konieczne.
> > > Czy to znaczy, że taki program jest równie dobry, jak program, w którym
przypisań nie ma?
> >
> > Nie rozumiem pytania.
>
> Krytykujesz operacje przypisania jako zły nawyk albo konstrukcję niepotrzebną i
nadmiarową. Tak przynajmniej rozumiem ten (pod)wątek.
Nie. Krytykuję stosowanie operacji przypisania tam, gdzie można
tego uniknąć, jako zły nawyk.
Na początku dyskusji podałem link do swojego wpisu na Quorze,
w którym odnosiłem się do implementacji algorytmu A* w C++.
Autorzy krytykowanego fragmentu kodu mieli jakieś dwie globalne
tablice, w których coś tam trzymali. Dzięki temu interfejs użycia
funkcji, oprócz argumentów, obejmował inicjalizację zewnętrznych
globalnych struktur danych. Jest to jedna z gorszych rzeczy
dla modularności, jakie można zrobić. I jest to kwestia wyłącznie
złego nawyku.
Podobnie złym nawykiem jest stosowanie arbitralnych kodowań, które
było zarówno w prezentowanym kodzie, jak i w skrytykowanej przeze
mnie książce Stroustrupa (który gdzieś tam sobie wymyśla, żeby
używać znaczku '8' do reprezentowania czegoś tam)
Jak sam zauważyłeś, interfejs do kodu liczącego MD5 jest czystą
funkcją (nawet jeśli pod spodem znajduje się jakaś maszyna stanów).
I to jest dobry nawyk, że nawet jeśli w implementacji czegoś
użyjesz jakichś stanów, to nie wypychasz tego do użytkownika.
AK tutaj równolegle do nas napisał coś takiego:
from itertools import count, islice
N = 15000
pierwsze = []
def jest_pierwsza(liczba, pierwsze=pierwsze):
czy_pierwsza = all(liczba % pierwsza for pierwsza in pierwsze)
if czy_pierwsza: pierwsze.append(liczba)
return czy_pierwsza
pierwsze = (liczba for liczba in count(2) if jest_pierwsza(liczba))
pierwszeN = (liczba for liczba in islice(pierwsze, N))
kwadraty = (liczba**2 for liczba in pierwszeN)
suma = sum(kwadraty)
print(suma)
Jak widać, jest tutaj sobie globalna tablica o nazwie "pierwsze",
która gdzieś tam jest modyfikowana. W takim małym przykładzie
może nie jest to szkodliwe, ale przy budowaniu większych systemów
już tak.
Bardzo dużo widziałem kursów programowania, które skupiają się
na małych przykładach, które przy próbie skalowania powodowałyby
chaos.
> Natomiast ja się z taką tezą nie zgadzam i próbuję wykazać, że model programowania
z przypisaniami (ogólnie: imperatywny) jest nie tylko wartościowy, ale często po
prostu optymalny i nie tylko dlatego, że odzwierciedla działanie komputera, ale
dlatego, że właściwie odzwierciedla postawiony problem.
Jeżeli właściwie odzwierciedla postawiony problem, to w porządku.
> Dlatego oczekuję od języka programowania, że będzie to wspierał jako podstawową
operację i konsekwentnie odrzucam języki, które tego nie mają, jako niepraktyczne.
W porządku. Używaj sobie czego chcesz. To Twoja sprawa.
Ja natomiast mówię o perspektywie innej, niż Twoja.
Pytanie postawione w wątku brzmi "jaki język polecić początkującemu".
Operator przypisania w językach takich jak C czy Python jest
łatwo dostępny, i sprawia wrażenie, że jest prostą operacją.
Brutalna rzeczywistość jest jednak taka, że używanie operatora
przypisania stwarza możliwość do popełnienia wielu błędów,
i właśnie z tego powodu w pierwszych dwóch rozdziałach SICP
nie znajdziesz ani jednego użycia tego operatora, a kiedy wreszcie
zostaje wprowadzony, jest obwarowany ostrzeżeniami i przykładami
problemów, które może powodować. (I nie ma to najmniejszego
związku z językiem Scheme)
I nie, nie chodzi o to, żeby nigdy nie używać przypisania.
Pomijam kwestię, że dla osób, które uczyły się matematyki, zapis
w rodzaju
x = x + 1
jest po prostu równaniem pozbawionym rozwiązań, zaś jedynym
rozwiązaniem formuły
x = 2 * x;
jest x = 0.
> [*] No dobra, znalazłem:
>
> http://hackage.haskell.org/package/cryptohash-md5
>
> "The implementation is made in C with a haskell FFI wrapper that hides the C
implementation."
>
> No tak. Tak było chyba *praktyczniej*, prawda?
No najwidoczniej tak.
> Ale jest też wersja dla "purystów":
>
> http://hackage.haskell.org/package/pureMD5-2.1.3/src
/Data/Digest/Pure/MD5.hs
>
> Totalna masakra. Nie chcę też nawet pytać, co tam robią słowa "unsafe" i czy z
takimi słowami to na pewno nadal jest "pure".
>
> W skrócie: uciekanie od imperatywności jest sztuczne i szkodzi zdrowiu.
Jak kiedyś będę miał więcej czasu, to może przyjrzę się temu zagadnieniu bliżej.
(Ogólnie wygląda mi na to, że ten przykład powinien się dobrze nadawać
do wyrażenia w postaci funkcyjnej)
-
65. Data: 2019-01-04 12:50:34
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: AK <n...@n...net>
On 2019-01-04 10:25, AK wrote:
> On 2019-01-04 02:00, AK wrote:
>> W Pythonie wyglada ona np tak (pisane "z palca" o 15 min:):
>>
>> from itertools import count, islice
> Autopoprawka polepszajaca:
Co nagle to po diable. Tu wersja jeszcze badziej ulepszona:
from itertools import count, islice, chain
from math import sqrt
N = 15000
pierwsze = [2]
def jest_pierwsza(liczba, pierwsze=pierwsze):
erasto = int(sqrt(liczba) + 0.5)
czy_pierwsza = all(liczba % pierwsza for pierwsza in pierwsze if
pierwsza <= erasto)
if czy_pierwsza: pierwsze.append(liczba)
return czy_pierwsza
pierwsze = chain([2], (liczba for liczba in count(3, step=2)
if jest_pierwsza(liczba)))
pierwszeN = (liczba for liczba in islice(pierwsze, N))
kwadraty = (liczba**2 for liczba in pierwszeN)
suma = sum(kwadraty)
print(suma)
AK
-
66. Data: 2019-01-04 13:29:59
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: g...@g...com
W dniu piątek, 4 stycznia 2019 02:00:15 UTC+1 użytkownik AK napisał:
> On 2019-01-03 21:51, fir wrote:
> >
> > w c taki programik nie wyglada zbyt tragicznie
> >
> >
> > int PoliczSumeParuPoczatkowychLiczbPierwszych(int ilu)
> > {
> >
> > int dodano_pierwszych =0;
> > int suma = 0;
> >
> > for(int i=0;;i++)
> > {
> > if(jest_liczba_pierwsza(i))
> > {
> > suma+=i*i;
> > dodano_pierwszych++;
> > if(dodano_pierwszych==ilu) return suma;
> > }
> > }
> > }
> >
>
> Jakto nie? Koszmar po prostu.
Szkoda czasu na dyskutowanie o gustach.
> > to ze nie wydziela on etapow na podej pierwsze, podnies do kwadratu zsumuj wynika
raczej z tego ze
> > pisze sie to tak by dzialalo szybko.. jak ktos sie nie upiera by bylo tak szybko
moze podzielic
> > na te fazy
>
> Ciezki niereformowalny betonie (zakalo tej grupy):
Chyba nie ma potrzeby zwracania się do siebie w taki sposób.
Za coś takiego nie daję szacunku.
> Wbij sobie do twego asemblerowatego lba ze mozna i szybko i z
> eleganckim rozdzialem na etapy.
> Oczywiscie w porzdiejzych jezykach od tego syfu C/C++.
> W dodatku wcale nie funkcyjnych, bo to raczez nie stricte funkcyjnosc
> jezyka jest zrodlem sukcesu, ale cos co w C++ nie wystepuje, a zwie
> sie generateory/wyrazenia generatorowe.
> Idea znana od dziesiecioleci (ale nie Ayatollahom C/C++).
>
> W Pythonie wyglada ona np tak (pisane "z palca" o 15 min:):
Nawet w Pythonie została dodana dość późno.
> from itertools import count, islice
>
> N = 15000
>
> pierwsze = []
> def jest_pierwsza(liczba, pierwsze=pierwsze):
> czy_pierwsza = all(liczba % pierwsza for pierwsza in pierwsze)
> if czy_pierwsza: pierwsze.append(liczba)
> return czy_pierwsza
>
> pierwsze = (liczba for liczba in count(2) if jest_pierwsza(liczba))
> pierwszeN = (liczba for liczba in islice(pierwsze, N))
> kwadraty = (liczba**2 for liczba in pierwszeN)
> suma = sum(kwadraty)
>
> print(suma)
w Haskellu chyba zwięźlej:
sieve (first:rest) = first:(sieve [x | <- rest, x `mod` first /= 0])
primes = sieve [2 ..]
-- nie chcemy wypisać "primes", bo jest ich nieskończenie wiele,
-- dlatego weźmiemy pierwszych 7:
take 7 primes -- dostajemy [2,3,5,7,11,13,17]
-- zaś cały przykład to będzie:
foldr (+) 0 (map (^2) (take 7 primes))
-- albo jak ktoś nie lubi nawiasów
foldr (+) 0 $ map (^2) $ take 7 primes
> No to teraz czekam palancie az usyskasz cos chocby zblizonego
> w C (w C++ jest latwiej, ale bez zewnetrzych bibliotek
> "metaprogramowania" sie nei obejdze, a i ta bedzie to potworek
> w stosunku do w/w w Pythonie.
>
> PS: Od lat 90tych istnieje jezyk w ktorym w/w bedzie jeszcze
> prostrze/czytelniejsze (o ktorym oczywiscie tez nie masz pojecia,
> jak kazdy"zakochany" w bitach:) i ktory wcale nie jest funkcyjny,
> ale jak najbardziej (jak i Python) imperatywny (zwal jak zwal).
Jaki język masz na myśli?
Ogólnie określenia "język funkcyjny" czy "język imperatywny"
nie są zbyt użyteczne.
W Haskellu można programować imperatywnie. W C można programować
funkcyjnie. To nie język, ale to, w jaki sposób go używamy,
jest kluczowe.
(No może są wyjątki. Na przykład język szablonów C++ jest czysto funkcyjny,
a asembler x86 jest czysto imperatywny)
> Sam se napisz leniu i niedouku!
Nie wiem co ma wynikać z rzucania tego rodzaju określeniami.
Jak ktoś mówi, że chciałby coś zobaczyć, to pewnie adresuje
swoją wypowiedź do kogoś, kto mu to może pokazać.
W czym problem?
-
67. Data: 2019-01-04 13:34:49
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: fir <p...@g...com>
W dniu piątek, 4 stycznia 2019 09:40:16 UTC+1 użytkownik g...@g...com napisał:
> 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 ..])))
>
to juz lepiej... w ym widze jakis wiekszy sens
trzebbylo zaczac od tego, bo trzeba przyznac ze kolegi zdolnosci tlumaczenia spraw
przypominaja zdolnosci tlumaczacego-inwalidy
kolega moze tgo nie widzi ale to powoduje ze ja np rozumiem reakcje ludzi na to co
wuglada jako strumien bzdetów dlatego ze jest zle pisane (zamiast podwac fakty
kluczowe i prosto kolega wypisuje te niektuczowe w inwalidzki sposob i predzej mozna
sie nabawic urazy do jezykow funkcyjnych czytajac to niz przeciwnie
- bo pamietam kiedys sam zajrzalem na jakis tutorial do lispa i nie wygladalo to tak
zle jak w wydaniu kolegi, tyle ze czego tam sie nauczylem i tak w czesci zapomnielem)
> 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
alez debilna wypowiedz... kolega powinien uwazac bo czasami osiaga poziom quelqebuga
czy innego talatajstwa a na tym poziomie ja nie moge rozmawiac
religie nie jest oparta na niewierzeniu,
religie jest oparta na wierze; to pierwsze to wrecz cos dokladnie przeciwnego i jest
to raczej wyraz sceptyzyzmu wobec wiary... jesli kolredze sie trafiaja takie babole w
wypowiedziach
to ostroznosc wobec przyjomania tych tekstow choc i tak wygladaja one nieco
podejrzanie jako rozumny przekaz staje sie tym bardziej uzasadniona... i ogolnei
zachodzi pytanie czy kolega bardzij mowi z sensem czy bardziej truje ludzi
bulszitem... (najprawdopodobniej jest to pewien mix ale z miksami bywa tak ze
domieszka bulshitu moze powodowac ze czytanie calosci jest bardziej stratnie niz
korzystne ;c)
ogolnie nietstsy rozmawianie z takimi ludzmi jest dosyc tragiczne
> 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.
no cos, mogłbym wspolczuc ale mi sie nie chce... obecnie zastanawiam sie czy kolega
ma jakas dysfunkcje mozgu i poprostu lubi lispa czy tez moze nauka lispa wywolala ta
dysfunkcje czy cos w tym stylu
samo to w sobie nie bylo by takie zle ale jesli kolega czestuje ta dysfunkcjonalnosci
ainnych to juz jest pewien trolling (tak to nazwijmy, nie uzwywam zbyt dopasowanych
okreslen bo
nie che tracic czasu na te glupie gadki)
normalny czlowiek moim zdaniem nie powinien miec problemow z rozumieniem na czym
polega c,
zgadza sie ze jezyk c jest w pewnym sensie kryptyczny i ze w pewnym sensie jest to
zle... kwestia jest jednak tak ze
c powstal przy okreslonych zalozeniach,
c nie tylko ma wyrazac zapis jakiegos tam taska do wykonania ale umozliwic zapis w
sposob odwolujacy sie do pewnego typu elementarnych operacji natywnych dla maszyny (o
czym chyba kazdy powinien wiedziec) i w tym sensie taki programik
jak wyzej jest naprawde lekki jest to pare instrukcji asemblera na krzyz, wyrazone w
postaci programu w c
c jest wlasnie do tego i tutaj lisp nie ma z c szans
zaznaczam ze do lispa nic nie mam, mam problem jednak z chrzanieniem - kolaga ma
powazny z tym problem, bierze jakas tematyke i zmieni ja w chrzanienie zamiast jednak
psiac o tym duzo bardziej normalnie ;c
sama ta linijka
> foldr (+) 0 (map (^2) (take 7 (filter isPrime [2 ..])))
jest ok, coz moge powiedziec jest to jedna z mozliwych opcji budowania jezyka
skryptowego, ktora ma zapewne w pewnych tematach rzeczywiste zalety, w innych inne
podejscia raczej by to przebily
oolenie to ta linijka przypomina troche skladanie i kombinowanie programow z jakiegos
basha (lub specyficzny sposob skladanie procedur jedna z druga:
innymi slowy przypomina to troche specyficzny pionowy sposob pisania
pewnych kawalkow w c
typu
x = limit(7)
l = primes(l)
l = square(l)
z = sum(l)
ew s = sum(square(primes(limit(7))))
tylko wyrazony poziomo i generalizowany (byc moze w ciakawy sposob, tj ktos tam
odwalil troche roboty by to podefiniowac),
nie mam nic do tego ale chrzanienie zbudowane dookola tego to nie jest dobra rzecz
-
68. Data: 2019-01-04 13:47:27
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: fir <p...@g...com>
W dniu piątek, 4 stycznia 2019 13:30:00 UTC+1 użytkownik g...@g...com napisał:
> > Sam se napisz leniu i niedouku!
>
> Nie wiem co ma wynikać z rzucania tego rodzaju określeniami.
>
> Jak ktoś mówi, że chciałby coś zobaczyć, to pewnie adresuje
> swoją wypowiedź do kogoś, kto mu to może pokazać.
>
> W czym problem?
aw to znany knajacki emeryt, jakas taka postac jak w tych pogramach o emertyach na
polsacie czy tvnie, 'trudne sprawy', 'wakacje z dziadami' 'polskie wakacje' czy cos w
tym stylu (bylo cos takiego z czego pochodizl mem o 'mięsnym jezu', te klimaty)
niech knajaczy i zyje w zludzeniu ze ja to czytam, powyobraza to sobie pare godzin
pozniej mu przejdzie - ja szczerze mowiac nie czytam tego bo mam duzo ciekawsze (tj
mniej glupi) rzeczy do czytania niz tak osobliwe rozrywki jak poczytywanie knajakow w
necie,
szczerze mowiac to jzu czytanie kolegi troche przekracza moja wytrzymalosc na
czytanie slabych tekstow ale jeszcze sie troche pomęcze
-
69. Data: 2019-01-04 13:52:21
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: g...@g...com
W dniu piątek, 4 stycznia 2019 13:47:29 UTC+1 użytkownik fir napisał:
> W dniu piątek, 4 stycznia 2019 13:30:00 UTC+1 użytkownik g...@g...com
napisał:
> > > Sam se napisz leniu i niedouku!
> >
> > Nie wiem co ma wynikać z rzucania tego rodzaju określeniami.
> >
> > Jak ktoś mówi, że chciałby coś zobaczyć, to pewnie adresuje
> > swoją wypowiedź do kogoś, kto mu to może pokazać.
> >
> > W czym problem?
>
> aw to znany knajacki emeryt, jakas taka postac jak w tych pogramach o emertyach na
polsacie czy tvnie, 'trudne sprawy', 'wakacje z dziadami' 'polskie wakacje' czy cos w
tym stylu (bylo cos takiego z czego pochodizl mem o 'mięsnym jezu', te klimaty)
>
> niech knajaczy i zyje w zludzeniu ze ja to czytam, powyobraza to sobie pare godzin
pozniej mu przejdzie - ja szczerze mowiac nie czytam tego bo mam duzo ciekawsze (tj
mniej glupi) rzeczy do czytania niz tak osobliwe rozrywki jak poczytywanie knajakow w
necie,
> szczerze mowiac to jzu czytanie kolegi troche przekracza moja wytrzymalosc na
czytanie slabych tekstow ale jeszcze sie troche pomęcze
Mhm.
A rozumiem, że to, co napisałeś w tym poście, to są mocne teksty?
-
70. Data: 2019-01-04 14:01:10
Temat: Re: Jaki język polecić początkującemu? - komentarz do artykułu w Programista 9/2018
Od: fir <p...@g...com>
W dniu piątek, 4 stycznia 2019 13:52:23 UTC+1 użytkownik g...@g...com napisał:
> W dniu piątek, 4 stycznia 2019 13:47:29 UTC+1 użytkownik fir napisał:
> > W dniu piątek, 4 stycznia 2019 13:30:00 UTC+1 użytkownik g...@g...com
napisał:
> > > > Sam se napisz leniu i niedouku!
> > >
> > > Nie wiem co ma wynikać z rzucania tego rodzaju określeniami.
> > >
> > > Jak ktoś mówi, że chciałby coś zobaczyć, to pewnie adresuje
> > > swoją wypowiedź do kogoś, kto mu to może pokazać.
> > >
> > > W czym problem?
> >
> > aw to znany knajacki emeryt, jakas taka postac jak w tych pogramach o emertyach
na polsacie czy tvnie, 'trudne sprawy', 'wakacje z dziadami' 'polskie wakacje' czy
cos w tym stylu (bylo cos takiego z czego pochodizl mem o 'mięsnym jezu', te klimaty)
> >
> > niech knajaczy i zyje w zludzeniu ze ja to czytam, powyobraza to sobie pare
godzin pozniej mu przejdzie - ja szczerze mowiac nie czytam tego bo mam duzo
ciekawsze (tj mniej glupi) rzeczy do czytania niz tak osobliwe rozrywki jak
poczytywanie knajakow w necie,
> > szczerze mowiac to jzu czytanie kolegi troche przekracza moja wytrzymalosc na
czytanie slabych tekstow ale jeszcze sie troche pomęcze
>
> Mhm.
> A rozumiem, że to, co napisałeś w tym poście, to są mocne teksty?
skad mam wiedziec co kolega naprawde rozumie... ciezko zgadnac..wyglada to szczerze
mowiac dosyc srednio
to teksty ktore ja napisalem nie sa
mocne bo sa to dorazne komentarze do produkowania babolastych ogolnikow,
(czyli czesciowo zle powyciaganych wnioskow...bo wypowiedzi kolegi moim zdaniem
skladaja sie jakby z materialu oryginalnego (jakiejs tam wiedzy na temat lispa i
innych aspektow programowania)
i powyciaganych przez kolege wnioskow
ktore sa ma zle powyciagane lub co najmniej zle uzywane i to jest juz problem, bo
bardzo zle sie rozmawia z kims kto w wojowniczy sposob zarzyuca
cie wnioskami blednymi lub zle uzywanymi
tak jak kolega robi
ja tak nie robie i to czyni moje wypowiedzi solidniejszymi ;c