A nejlepší na tom je, že generátory a iterátory, tedy základní kameny pro podobné "nekonečné sekvence" byly do Pythonu přidány v roce 2001, 6 let před vznikem Clojure. Tyhle předpřipravené funkcičky se každopádně hodí, i když se dají implementovat skutečně snadno.
12. 9. 2023, 10:40 editováno autorem komentáře
Otázka je možná jak často jsou v běžném Pythonu - v porovnání s Clojure - používány. Třeba v Common Lispu jsou series - potenciálně nekonečné sekvence - přes čtyřicet let (sice "jen" knihovna, ale kdysi příloha standardu), a prostě to skoro nikdo nepoužívá.
I když možná to bylo příliš ambiciózní - pokud to šlo tak se to pokoušelo automaticky transformovat kód typu generátor-mapování-collector na smyčku, a snažilo se to tlačit uživatele k psaní funkcí které to podporovaly, což bylo nepohodlné.
Ano, samozřejmě. Já tohle (generátory apod.) ale používám v kódu skoro hned od zavedení. Pro jiné účely než zde naznačené, ale princip je stejný. Myslím, že spousta programátorů v Pythonu ani nemá představu, jaké nástroje jsou k dispozici (nemyslím jenom tooly, ale knihovní záležitosti, třeba collections.Counter).
Pretože Python nie je vnímaný ako funkcionálny jazyk. Podľa mňa všetci, ktorí sa zaujímajú o funcy už majú skúsenosti s inými funkcionálnymi jazykmi. Bežný Python programátor vôbec netuší o čo tu vlastne ide. Určite tiež nepomáha fakt, že je to strčené do nič nehovoriacich modulov ako itertools, moreitertools atď.
V protiklade s tým, napr. jazyk Groovy, tam to hneď bije do očí. Základná syntax pre prácu s zoznamami.
def vals = [1, 2, 3, 4, 5, 6, 7]
def isEven = { it % 2 == 0 }
def square = { it * it }
def res = vals
.findAll(isEven)
.collect(square)
.sum(0)
println res
Novší Groovy GINQ:
record Customer(Integer id, String name) {}
record Reservation(Integer id, Integer customerId, String date) {}
def customers = [
new Customer(1, 'Paul Novak'), new Customer(2, 'John Doe'),
new Customer(3, 'Jack Fonda'), new Customer(4, 'Roger Roe')
]
def reservations = [
new Reservation(1, 1, '2019-11-22'), new Reservation(2, 2, '2019-11-28'),
new Reservation(2, 1, '2019-11-28'), new Reservation(4, 1, '2019-11-29'),
new Reservation(5, 3, '2019-12-02'), new Reservation(6, 2, '2020-02-20')
]
def res = GQ {
from c in customers
join r in reservations on c.id == r.customerId
select c.Name as customers, r.Date as reservations
}
println res
Hneď od začiatku sa študent stretne s closures, pokročilou prácou s funkciami. Pritom je Groovy syntaxou dosť podobný Pythonu.
13. 9. 2023, 21:20 editováno autorem komentáře
To bych musel vědět, co máš konkrétně na mysli. Některé věci třeba standardní knihovna Pythonu "tak nějak" umí, ale není to dostatečné - např. pro práci s časem používám Pendulum, Python nemá dostatečnou podporu pro šifrování atd. Určitě jsem ale osobně spíš pro standardní prostředky tam, kde dostačují.
Sice šlo, ale...:
it=[42]*100_000_000 # Naalokovat 100M intů, prosím
it=[42]*100_000_000_000 # libo-li MemoryError?
it=[42]*float("inf") # Nekonečné sekvence nejen ze nejdou vypočíst
# ale nejdou ani vyjádřit, páč tuto vyhodí TypeError
Zmíněný repeat je importovaný přímo z itertools a funcy tu funkci "jen" znova publikují.
Kap. 11, otázky k zamyšlení: chybí mi tam samozřejmě nultá otázka (Proč tu není itertools.product? Co za magii se tu děje, že zipování cyklů vypadá líp?), ale určitě nebudou chodit soudělné délky...
>>> from funcy import take, cycle
>>> letters = "abcdef" # 2*3
>>> numbers = range(10) # 2*5
>>> n = len(letters) * len(numbers)
>>> uniq = set(take(n, zip(cycle(letters), cycle(numbers))))
>>> assert len(uniq) == n, f"Expected {n} items, got {len(uniq)} items."
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Expected 60 items, got 30 items.
... což je dost silné tvrzení, takže "nesoudělné délky všech iterátorů pod cykly" by mohla být dostačující podmínka. Ale prvočísla z příkladu od karet budou chodit určitě. Samy otázky čtenáře navádí, že dělat kartézský součin z cyklů není úplně šťastné :)
přesně tak, ono to chodí díky tomu, že každá barva má 13 karet, což zrovna tady je pěkné číslo.
A ne, to řešení není pěkné, právě proto je dobré se zamyslet, jestli se používá správná věc na správném místě.
O itertools bude samostatný článek (asi měl vyjít před funcy, to by bylo didakticky správnější).
Pro Waveleta verze v Leanu:
structure Sequence (α : Type u) where
value : α
next : α -> α
instance : Stream (Sequence α) α where
next? seq := some ⟨seq.value, ⟨seq.next seq.value, seq.next⟩⟩
def take (n : Nat) (seq : Sequence α) : Array α := Id.run <| do
let mut i := 0
let mut arr := #[]
for x in seq do
arr := arr.push x
i := i + 1
if i == n then break
arr
def main : IO Unit := do
let seq := Sequence.mk ⟨0, 1⟩ fun (x : Nat × Nat) => ⟨x.snd, x.fst + x.snd⟩
let arr := take 20 seq
IO.println s!"{arr.map (·.fst)}"13. 9. 2023, 19:46 editováno autorem komentáře
Tady je trochu lepší varianta:
structure Sequence (α : Type u) where
value : α
next : α α
instance : Stream (Sequence α) α where
next? seq := some ⟨seq.value, ⟨seq.next seq.value, seq.next⟩⟩
def take (n : Nat) (seq : Sequence α) : Sigma fun n => Vector n α := Id.run do
if let isTrue h := n.decEq 0 then return ⟨0, ⟨#[], rfl⟩⟩
let mut p := #[].toVector
let push (p : Sigma fun n => Vector n α) (x : α) : Sigma fun n => Vector n α :=
let ⟨n, v⟩ := p
let r := v.push x
⟨n+1, r⟩
for x in seq do
p := push p x
if let isTrue h := p.snd.size.decEq n then return ⟨n, h p.snd⟩
return ⟨p.snd.size, p.snd⟩
def main : IO Unit := do
let seq := Sequence.mk ⟨0, 1⟩ fun (x : Nat × Nat) => ⟨x.snd, x.fst + x.snd⟩
let stdin <- IO.getStdin
IO.print "How many Fibonacci numbers do you want to generate? "
let some n := (<- stdin.getLine).trim.toNat? | IO.eprintln "You need to enter a natural number."
let ⟨_, v⟩ := take n seq
let v := v.map (·.fst)
IO.println s!"{v}"
IO.print "Enter the index of an element: "
let some n := (<- stdin.getLine).trim.toNat? | IO.eprintln "You need to enter a natural number."
let some n := n.finFromNat? v.size | IO.eprintln "The index is out of bounds."
IO.println s!"{v.get n}"
Tohle je zajímavý talk. Pamatuju si, že Sweeney delal na tomhle "vlastnim" jazyce, ale pak uz poznali, ze maji limity a potrebovali teorii, tak najmuli Jonese. Problem je že syntakticky to je dost podivne pro vyvojare co jen skriptuji bezne v jinych hernich enginech -- proste funkconalni no. Rozhodne to neco podstatneho resi v jejich domene na kterou je to presne usite. Takze to muze mit i uspech -- narozdil od bambilionu obecnych jazyku. V tomhle je zajimavy treba i GOAL od Naughty Dogs.
Jo, vzhledem k tomu, ze Unity is dead, tak se budou muset rekvalifikovat na Unreal a funcionalni programovani. Pokud tedy nemigruji na Godot, coz asi dava vetsi smysl (a je to vic cool). Ale to zase minou to funkcionalni programovani :D
https://www.youtube.com/watch?v=KTWHdLZZdGw
16. 9. 2023, 20:53 editováno autorem komentáře