Lazy Evaluation in Python
18:06
4 жыл бұрын
Пікірлер
@st8113
@st8113 4 күн бұрын
That last one took me a moment We build a new list that excludes any paths away from current node - any path that would be immediately valid And then we pass that list into the recursive call, to make sure that in the next iteration, we don't try to traverse any path that would take us back to the original node
@SphereofTime
@SphereofTime 6 күн бұрын
0:41 types, evaluation haskell toolchain ghc ghci cabal
@inserteunnombreapropiado9079
@inserteunnombreapropiado9079 15 күн бұрын
Too late to comment, but I would have suggested to add a visual representation of the solutions as it's difficult to visualize them in my head. My take on 2nd: prefixes = rev . foldl (\acc x -> if null acc then acc ++ [[x]] else (head acc ++ [x]) : acc) [] My take on 3rd: lagrange :: [(Float, Float)] -> Float -> Float lagrange p x = foldl (\acc (xj, yj) -> acc + (yj * smolL xj)) 0 p where smolL :: Float -> Float smolL xj = foldl (\acc (xm, _) -> acc * (if xm == xj then 1 else (x - xm) / (xj - xm))) 1 p
@inserteunnombreapropiado9079
@inserteunnombreapropiado9079 21 күн бұрын
My take on the fourth (don't judge me, I'm just following): hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath nodes a b = let attached = [x | x <- nodes, fst x == a] in aux nodes attached a b where aux _ [] _ _ = False aux nodes (v:vs) a b | (a,b) `elem` (v:vs) = True | otherwise = let remains = [x | x <- nodes, x `notElem` vs && snd x /= a] in hasPath remains (snd v) b || aux nodes vs a b
@Gice-Music
@Gice-Music 22 күн бұрын
for exercise 3 wouldn't the type need to be isAsc' :: (Ord Int) => [Int] -> Bool in order to be able to use '<=' ? I get errors if I just use the type you provided'
@bryonrobidoux1021
@bryonrobidoux1021 27 күн бұрын
This was awesome and very helpful.
@real1cytv
@real1cytv 28 күн бұрын
"You can do this just as well imperatively, but that's not the point" seems to be Haskells mantra 😂
@Farligefinn
@Farligefinn Ай бұрын
I did: getWords xs = do a <- getLine if null a then return xs else do getWords (a:xs) And called it with: getWords []
@EvanZamir
@EvanZamir Ай бұрын
Take my value. Please. 😉
@skorp5677
@skorp5677 Ай бұрын
In case anyone is wondering why it is called PeaNums: The natural numbers are constructed by the Peano Axioms, which are basically rules on what the naturals are and how they behave :) en.wikipedia.org/wiki/Peano_axioms
@Code_Nybble
@Code_Nybble Ай бұрын
Super happy I signed up for the functional programming elective next quarter, this really combines my favorite elegant parts of maths with programming! Amazing videos, very nice and simple to follow with just the right amount of challenge.
@Merlin-gl7zp
@Merlin-gl7zp 2 ай бұрын
Haskell is lazy, That just like us programmers, that's why we love haskell!
@jakubvitovec8297
@jakubvitovec8297 2 ай бұрын
My solution of excercise 4 is heavilly affected by procedural code and I was having trouble to switch my brain to this functional style. So my horrible solution is : hasPath:: [(Int, Int)] -> Int -> Int -> Bool hasPath [] _ _ = False hasPath arr start end = findPath arr arr start end start Data.Set.empty where findPath:: [(Int, Int)] -> [(Int, Int)] -> Int -> Int -> Int -> Data.Set.Set Int -> Bool findPath [] _ _ _ _ _ = False findPath ((x,y):xs) original start end searching visited --Searching is the start point in the beginning | searching == y && end == y = True --We found the path | start == x && end == y = True | searching == x && (member y visited) = False --We cycled | searching == x = (findPath xs original start end y (Data.Set.insert x visited)) || (findPath original original start end y (Data.Set.insert x visited)) --Either find next from the start or in the rest of the current one or find next occurence of x, one must be true to find the path | otherwise = findPath xs original start end searching visited
@Merlin-gl7zp
@Merlin-gl7zp 2 ай бұрын
Unfortunately `length ones` freezes ghci :>. + fun fact: ghci starts the compiler in another process in linux, so it continues to run on its own after terminating ghci (calculating the infinite list)
@Merlin-gl7zp
@Merlin-gl7zp 2 ай бұрын
3:00 My solution, i was struggling with `rev` ```Haskell unwrap :: [[a]] -> [a] unwrap [] = [] unwrap [] (x:xs) = x prefixes list = rev (foldl (\acc x -> (rev (x : (rev(unwrap acc)))) : acc ) [] list) ```
@liltimmy8546
@liltimmy8546 2 ай бұрын
this course is just great
@AlexEuler-hx7mo
@AlexEuler-hx7mo 2 ай бұрын
hasPath::[(Int, Int)] -> Int -> Int -> Bool hasPath [] s f = s == f hasPath ((b, e):xs) s f = hasPath xs s f || (hasPath xs s b && hasPath xs e f)
@shadow-ht5gk
@shadow-ht5gk 2 ай бұрын
Took a few watches to get this one down lol
@higiniofuentes2551
@higiniofuentes2551 2 ай бұрын
Using IO I suppose is the way to work with files, text, binary, CSV, XML, JSON, do you have any video around this? Thank you!
@higiniofuentes2551
@higiniofuentes2551 2 ай бұрын
Thank you for this very useful video!
@EpicNicks
@EpicNicks 2 ай бұрын
5:45 moan add
@seanmcghee2373
@seanmcghee2373 2 ай бұрын
Exercise 4 made it an imperative to not use Haskell...
@seanmcghee2373
@seanmcghee2373 2 ай бұрын
fst = first; snd = second. took me a mo...
@marvin.mcfly7749
@marvin.mcfly7749 3 ай бұрын
Have things changed in last 3 years? The first solution is now throwing errors like "ambiguous occurrence 'elem') EDIT: looks like 'elem' is overloaded. Changing it to 'element' fixed the issue.
@HyperSimplex
@HyperSimplex 3 ай бұрын
great videos!
@nincako
@nincako 3 ай бұрын
this video is NOT about application. The codes given are just definitions and it is heavily based on describing something very abstract. Please do not waste your time.
@mouduge
@mouduge 3 ай бұрын
Awesome tutorial! However, I'm really not sold on Haskell's error management, it feels like a bad compromise between "errors as data" and "errors as special control flow". I think exceptions are a really bad idea, they go against the spirit of functional programming, they're easy to mishandle, they complexify many situations, they kill kittens, and the world would be better off without them. Your justification for them at 9:55 is that "if the whole runtime is failing, then only exceptions get through". But why? Why couldn't errors be returned as data? Rust manages it, it's definitely possible. IMHO, the main downsides of "errors as data" are: 1) If function f1 calls function f2 which calls function f3, and so on up to function f100, which returns an error that needs to bubble back up to f1, then all the intermediate functions f2 to f99 have a bit of work to do, which exceptions can avoid. But some synctactic sugar can make this as easy as a single character, for example Rust has the ? operator: when f99 calls f100(...)?, if the return value of f100(...) is an error, then f99 immediately returns this error. There's also map_err in case you need to tweak the error before returning it. This makes the pass-through very lightweight, and I actually like the fact that it's explicit. Exceptions can go through unnoticed, which leads to programming errors. 2) If a function h calls multiple functions g1, g2, g3, ..., g100, which may return errors E1, E2, ..., E100 respectively, and if we suppose that function h just wants to let these errors bubble up to the caller, then its error return type will be E1 | E2 | E3 | ... | E100. In Haskell you will have to define a custom data type just for function h, for example `data ErrorH = ErrorH1 E1 | ErrorH2 E2 | ... | ErrorH100 E100`. If another function G may return errrors E2 | E3 | ... | E101, then it will need its own type ErrorG, which will be treated as completely different from ErrorH by the compiler. Sadly, Haskell does not seem to support such "structural sum types" (e.g., as opposed to languages like Typescript, Flow, Elm, ROC, or Julia, which let you use E1 | E2 | E3 as a perfectly valid type). Please correct me if Haskell does have structural sum types, that would be great! Without structural sum types, you have to deal with every possible combination of errors with its own new type. I can see why exceptions are tempting in this context. Note that Rust doesn't support structural sum types either, and therefore you have to use hacky external libraries to work around these difficulties. And this limitation encourages people to bundle all related errors into a single type, like IOError, you lose a lot of granularity. In short, I don't like Rust's error system either. I haven't played with ROC yet, but its error management looks fabulous. There are no exceptions, it's just errors-as-data, with nice synctactic sugar to make passthrough pretty lightweight (+ explicit + flexible), and with structural sum types that are automatically inferred by the compiler, and let you be as granular as you want with your errors, without any typing nightmare, and proper exhaustivity checks to ensure every error is dealt with. Check out this great talk for more details: kzbin.info/www/bejne/bYTMlYasf8iDmpI Perhaps Haskell could evolve in that direction too?
@mouduge
@mouduge 3 ай бұрын
Excellent series, thanks so much! Regarding your warning about `seq` at 8:49, I'm not sure exactly *what the risks are* and *what to do* about them? ➤ Risks → If I understand correctly, using `seq` might sometimes degrade performance (speed or RAM usage) because it messes with the compiler's assumptions. That's the only risk, right? Or is there also a risk of compilation or runtime errors? ➤ What to do → Should I use lazy code by default, and if I hit performance issues, first try to turn the compilation optimisations on (which may automatically make things stricter when needed), and if that's not enough, then try using strict code and see if that improves things?
@mouduge
@mouduge 3 ай бұрын
Great series, thanks! 👍 A note for Linux newbies: at 10:20, when you type `USER="Haskell user"`, you are assuming that the `USER` environment variable has already been exported. This only works because this is a pretty standard variable that is usually exported in your system's init scripts. But if you were to use a different variable name, like `GREET_USER`, then you would have to type `export GREET_USER="Haskell user"`, or else the variable would only be visible to the current shell, not to subprocesses like `greet`. This is why you had to type `export USER` at 10:32, after the variable was `unset`. Once a variable is exported, you can change its value without exporting it again, and the updated value will be available to subprocesses. Alternatively, you could set the variable only for the subprocess like this: GREET_USER="Haskell user" ./greet Hope this helps someone.
@carlorosso2413
@carlorosso2413 3 ай бұрын
In the third exercise I understood why you say that Haskell is pure: lagrange :: [(Float, Float)] -> Float -> Float lagrange xs x = sum [y_i * lagrangeBasis xs x_i x | (x_i, y_i) <- xs] where lagrangeBasis xs x_i x = product [lagrangeOne x_i x_j x | (x_j, _) <- xs, x_j /= x_i] lagrangeOne x_i x_j x = (x - x_j) / (x_i - x_j)
@Mr.Beandip-ve9iz
@Mr.Beandip-ve9iz 4 ай бұрын
This is great. It's the first course I've found that actually compares Haskell to what I already know. Programmers everywhere thank you.
@en8042
@en8042 4 ай бұрын
I came up with this solution, I hope it's correct I haven't yet found an input for which it isn't. hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath [] x y = False hasPath (x:xs) u v | (fst x == u) && (snd x == v) = True | (fst x /= u) = hasPath xs u v | otherwise = hasPath xs u v || hasPath xs (snd x) v wrapper :: [(Int, Int)] -> Int -> Int -> Bool wrapper xs u v = hasPath (xs ++ xs) u v If there are no edges, then there are no routes. If there is at least one edge and its first vertex is the start node and its second vertex is the end node, we're done. If the first vertex isn't the start node, we just skip this edge and try to find the path in the rest of the edges. Otherwise we start a search in the rest of the edges with both the first and second vertex. We also need a wrapper function which just calls into hasPath, except it doubles the edge list, since it might be possible that ignored edges in the beginning might be needed later, in other words we make sure we visit all pairs of edges in both order. Correction: doubling the list is not enough, I think we need to multiply the list by the number of its elements (number of edges).
@Doodoofart725
@Doodoofart725 4 ай бұрын
I wonder where all the discarded thunks end up 😢
@exbibyte
@exbibyte 4 ай бұрын
🔥thank you for the videos
@Russoski234
@Russoski234 4 ай бұрын
My solution seems to work, although it's simpler. For directed graphs, the tuple (a,b) indicates the directed edge from a to b. With this in mind, if I can find a road in which I can start from a tuple with a as a source and go to another tuple while updating my source, it is just a matter of time until i find the case in which the tuple (updated_src, dst) is exactly a member of the list. It works with the list of tuples [(1,2),(2,3),(3,2),(3,4)] and returns True if i search for 1 4 and 1 3, but i don't know if it's bulletproof. Thanks for the videos! hasPath::[(Int,Int)] -> Int -> Int -> Bool hasPath [] _ _ = False hasPath (x:xs) src dst | fst x == src && snd x == dst = True | fst x == src && (fst x <= snd x) = hasPath xs (snd x) dst | fst x == src && (fst x >= snd x) = hasPath xs (fst x) dst | otherwise = hasPath xs src dst
@theantonlulz
@theantonlulz 4 ай бұрын
Exercise 2 is the prefect argument against functional programming.
@TheTIM333
@TheTIM333 3 ай бұрын
prefixes = [take m n | m <- [1..length n]] Nice, simple, elegant.
@shipweck6253
@shipweck6253 3 ай бұрын
@@TheTIM333 came up with that but then when i unpaused he said to specifically use folding and then i sat there and tried only for the actual solution in the video to be horribly unreadable and unconcise
@TheTIM333
@TheTIM333 3 ай бұрын
@@shipweck6253 I agree, it is not exactly the most elegant but this cannot be used as an argument against functional programming as using folding was arbitrarily imposed for the purpose of the exercise.
@theantonlulz
@theantonlulz 4 ай бұрын
This video ain't it. I understood virtually nothing about data types or their uses.
@capekraken2672
@capekraken2672 4 ай бұрын
Skill issue
@Nil44419
@Nil44419 4 ай бұрын
Hi, Now I checked this in ghci. let x = 1 + 1; let y = x + 5, after forcing y to be evaluated by typing y in ghci, :sprint y is still _. Edited: I didn't mentioned the type as Int. Now its working as expected.
@agaveboy
@agaveboy 4 ай бұрын
hint for understanding the 2D map: the map function itself is passed as an argument to the second map function - we are applying the map function to each element (list) of the 2D list.
@milosmrdovic7233
@milosmrdovic7233 4 ай бұрын
Answer #4 is needlessly complex and assumes there's always a path from x to x. I'd use something like this instead: hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath [] _ _ = False hasPath [(a, b)] x y = a == x && b == y hasPath (a:xs) x y | x == fst a = hasPath xs (snd a) y | otherwise = hasPath xs x y
@MatthewHaydenRE
@MatthewHaydenRE 5 ай бұрын
Great video series. Was there a conscious choice not to use function composition?
@idkwhattonamethisshti
@idkwhattonamethisshti 5 ай бұрын
Yeah, I give up. This is not understandable at all
@densidad13
@densidad13 5 ай бұрын
I've seen many videos in this series so far and have to congratulate you for the clear explanation style. I've heard about the STM paper before and think I'm now in a position to try to uderstand it. I'll like the refereces to the conceptual sources! Thank you.
@Bulbasauros
@Bulbasauros 5 ай бұрын
Ex2 doesn't remove duplicates.
@cordestandoff2358
@cordestandoff2358 5 ай бұрын
My solution of 4 ex: hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath array from to | null [x | x <- array, fst x == from] = False | ([x | x <- array, fst x == from] !! 0) == (from, to) = True | otherwise = ( do let from_array = [x | x <- array, fst x == from] let not_from_array = [x | x <- array, not (fst x == from)] let only_snds_from_array = [(snd x) | x <- from_array] let next_depth = [True | x <- only_snds_from_array, hasPath not_from_array x to] if (not (null next_depth)) then True else False ) main :: IO () main = do print (hasPath [(1, 2), (2, 3), (3, 2), (4, 3), (4, 5)] 1 3) So i just loop in checking of all branches, idk about speed of this sh*t, but hey! Im 15 y. o, and i just russian school boy
@becbelk
@becbelk 5 ай бұрын
very hard... lol....forgot recursion since 1997... need recycle my brain
@ihategoogle2939
@ihategoogle2939 5 ай бұрын
For doing question 3 with just the stuff discussed, I figured this would work isAsc :: [Int] -> Bool isAsc (x:xs) | null xs = True | x < head xs = isAsc xs | otherwise = False
@ale-hl8pg
@ale-hl8pg 6 ай бұрын
Thought I'd add my 2 cents here - higher order functions seem to make this counter-intuitive (at least with was super counter intuitive to me) This was completely counter intuitive to me because I thought map . map would result in a function which takes in a list, uses the second map on the list to produce a new one, then uses the first map on the new list to produce an even newer one This isn't the case because map is a higher order function, so with how it gets evaluated, you end up with a map that internally uses a map All the other comments explain that relatively well but imo it'd be hard to call this "analogous" to mathematical composition, it's a lot more general because higher order functions complicate things a lot - as seen with this example You're not saying that you're applying one map on a list then another map on the result of that list, you're saying that you're creating a new function that is a map, which ITSELF uses a map This is a composition of maps which maps a list of a's to a list of b's, then to a list of c's: mapTransitive:: (b -> c) -> (a -> b) -> a -> c mapTransitive f1 f2 = map f1 . map f2 This is a composition of maps which uses a map to map sublists to new lists so that you get a new list: map2D: (b -> c) -> (a -> b) -> a map2D = map . map The biggest difference is the way arguments get applied - that's what changes the outcome, the order matters a lot
@alessiodellicolli1593
@alessiodellicolli1593 6 ай бұрын
could this implementation of exercise 4 be good? Because it looks much simpler hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath [] y z = y==z hasPath ((x1, x2):xs) y z = y==z || (hasPath xs y x1 && hasPath xs x2 z) || hasPath xs y z
@hotdog9259
@hotdog9259 6 ай бұрын
At 10:00, you say that the tensor product is a functor, but maps objects of C. My understanding is that would be a morphism, not a functor? Or am I misunderstanding