- Видео 44
- Просмотров 1 449 058
Philipp Hagenlocher
Германия
Добавлен 5 янв 2020
Programming and Computer Science
Impressum: philipphagenlocher.de/impressum
Impressum: philipphagenlocher.de/impressum
Haskell for Imperative Programmers #43 - Cabal
This video is supported by translatebox.io
Cabal: www.haskell.org/cabal/
Install instructions (Windows): hub.zhox.com/posts/introducing-haskell-dev/
Editor shown in this video: vscodium.com
Further reading:
dev.stephendiehl.com/hask/#cabal
cabal.readthedocs.io/
Timestamps:
00:00 - Intro
00:50 - Installation & First Steps
02:07 - Project Setup
07:05 - Adding Library Modules
10:40 - Building a Library
13:21 - Commons
16:28 - Checking .cabal
17:50 - Multiple Executables
21:41 - Resolving Dependencies
24:35 - Test Suites with QuickCheck
33:34 - Some Considerations
35:22 - Cabal Hell
Cabal: www.haskell.org/cabal/
Install instructions (Windows): hub.zhox.com/posts/introducing-haskell-dev/
Editor shown in this video: vscodium.com
Further reading:
dev.stephendiehl.com/hask/#cabal
cabal.readthedocs.io/
Timestamps:
00:00 - Intro
00:50 - Installation & First Steps
02:07 - Project Setup
07:05 - Adding Library Modules
10:40 - Building a Library
13:21 - Commons
16:28 - Checking .cabal
17:50 - Multiple Executables
21:41 - Resolving Dependencies
24:35 - Test Suites with QuickCheck
33:34 - Some Considerations
35:22 - Cabal Hell
Просмотров: 10 403
Видео
Haskell for Imperative Programmers #42 - QuickSpec
Просмотров 3,1 тыс.3 года назад
This video is supported by translatebox.io QuickSpec: hackage.haskell.org/package/quickspec Code shown in the video: github.com/phagenlocher/quickspec-examples Examples: github.com/nick8325/quickspec/tree/master/examples Timestamps: 00:00 - Intro 04:25 - Simple Laws for the Addition 05:58 - concatMap Laws, Polymorphic Signatures 09:51 - Laws on our own Code 11:36 - Predicates 16:26 - Laws on fo...
Haskell for Imperative Programmers #41 - Formal Verification (using Isabelle)
Просмотров 7 тыс.3 года назад
This video is supported by translatebox.io Isabelle: isabelle.in.tum.de The Archive of Formal Proofs: www.isa-afp.org Timestamps: 00:00:00 - Intro 00:03:25 - First Steps in Isabelle 00:14:10 - Simple Induction Proof 00:21:15 - Computation Induction 00:28:00 - A failed Induction 00:35:55 - Accumulator Generalization 00:54:10 - Termination Proof 01:04:06 - Proofs on the Sortedness of Trees 01:17:...
Haskell for Imperative Programmers #40 - Termination Proofs
Просмотров 3,4 тыс.3 года назад
This video is supported by translatebox.io Further reading: www.springer.com/de/book/9783658263010 en.wikipedia.org/wiki/Termination_analysis en.wikipedia.org/wiki/Total_functional_programming Timestamps: 00:00 - Intro 01:06 - Non-Termination and Undefinedness 03:07 - What is Termination? 05:54 - Definition for Termination Proofs 07:09 - Factorial Proof 10:04 - Second Proof 12:36 - Precondition...
Haskell for Imperative Programmers #39 - Induction Proofs
Просмотров 8 тыс.3 года назад
This video is supported by translatebox.io Further reading: en.wikipedia.org/wiki/Well-founded_relation en.wikipedia.org/wiki/Ascending_chain_condition en.wikipedia.org/wiki/Structural_induction Timestamps: 00:00 - Intro 02:18 - The Induction 02:54 - Noetherian Induction 03:55 - Structural Induction 05:36 - Example of structural induction 07:12 - Problems with our proof 08:09 - Computation Indu...
Lazy Evaluation in Python
Просмотров 5 тыс.4 года назад
Python Code: gist.github.com/phagenlocher/8532fe65a5bc99cb5f835441c950baf6 Java Code: gist.github.com/phagenlocher/5c8b03d6174ea77cfa4cee4b6bc633d3 Timestamps: 00:00 - What is Lazy Evaluation? 01:42 - Functions and Thunks 03:29 - The Lazy Class 08:27 - Factorials Done Lazily 09:29 - A List of Lazy 10:12 - Infinite Lists 11:59 - A Whole Lotta Generators 15:00 - Some Considerations Support me on ...
Haskell for Imperative Programmers #38 - Monad Transformers
Просмотров 16 тыс.4 года назад
Autobots, roll out! More reading: wiki.haskell.org/All_About_Monads#Monad_transformers en.wikibooks.org/wiki/Haskell/Monad_transformers Timestamps: 00:00 - Combining IO and Maybe 02:17 - Maybe Transformer 05:12 - State Transformer 08:44 - More Transformers 09:19 - Transformers are experimental! Support me on Ko-fi: ko-fi.com/phagenlocher
Haskell for Imperative Programmers #37 - Arrows
Просмотров 11 тыс.4 года назад
Let's head in the right direction! Programming with Arrows by John Hughes: www.cse.chalmers.se/~rjmh/afp-arrows.pdf Generalising monads to arrows by John Hughes: www.sciencedirect.com/science/article/pii/S0167642399000234 Easier reading: en.wikibooks.org/wiki/Haskell/Understanding_arrows www.haskell.org/arrows/index.html Timestamps: 00:00 - What are Arrows? 00:58 - Arrow Typeclass 03:05 - Categ...
Haskell for Imperative Programmers #36 - Category Theory (Functors, Applicatives, Monads)
Просмотров 25 тыс.4 года назад
In this video we are going to get theoretical! Programming with categories: ruclips.net/p/PLhgq-BqyZ7i7MTGhUROZy3BOICnVixETS Category theory for programmers by Bartosz Milewski: github.com/hmemcpy/milewski-ctfp-pdf Seven Sketches in Compositionality by Brendan Fong & David I. Spivak: arxiv.org/pdf/1803.05316.pdf Applicative programming with effect by Conor McBride & Ross Paterson: www.staff.cit...
Haskell for Imperative Programmers #35 - Semigroup & Monoid
Просмотров 14 тыс.4 года назад
In this video it's going to get theoretical! Documentation: hackage.haskell.org/package/base-4.14.0.0/docs/Data-Semigroup.html hackage.haskell.org/package/base-4.14.0.0/docs/Data-Monoid.html Further reading: en.wikipedia.org/wiki/Algebraic_structure Timestamps: 00:00 - Intro 01:04 - Algebras and their definition 05:10 - Magma definition 05:35 - Semigroup definition 07:18 - Semigroup examples 09...
Haskell for Imperative Programmers #34 - Profiling
Просмотров 4,3 тыс.4 года назад
In this video we stare into the abyss until it stares back into us. ThreadScope: github.com/haskell/ThreadScope/releases Documentation: downloads.haskell.org/~ghc/latest/docs/html/users_guide/profiling.html Timestamps: 00:00 - Intro 01:41 - Measuring Execution Time 08:17 - Cost Centre Stack 13:03 - Measuring Heap Allocation 16:37 - Profiling the Garbage Collector 21:01 - Code Coverage 25:40 - P...
Haskell for Imperative Programmers #33 - Parallelism
Просмотров 8 тыс.4 года назад
Considering the length of this video watching at 2x speed is recommended! ;) ThreadScope: github.com/haskell/ThreadScope/releases Documentation: hackage.haskell.org/package/parallel-3.2.2.0/docs/Control-Parallel-Strategies.html Further reading: wiki.haskell.org/ThreadScope wiki.haskell.org/ThreadScope_Tour Timestamps: 00:00 - Theory on Parralelism 03:47 - Eval Monad and Strategies 09:37 - Spark...
Haskell for Imperative Programmers #32 - DeepSeq
Просмотров 4,3 тыс.4 года назад
In this video we are going to evaluate to normal form. Documentation: downloads.haskell.org/~ghc/7.10.1/docs/html/libraries/Control-DeepSeq.html Timestamps: 00:00 - Intro 00:51 - Documentation 04:22 - Basic Example 08:36 - Peano Example 11:34 - Tree Example 16:20 - Usage with IO Actions 20:35 - Discussion on Usage Support me on Ko-fi: ko-fi.com/phagenlocher
Haskell for Imperative Programmers #31 - Weak Head Normal Form
Просмотров 8 тыс.4 года назад
In this video we are going to discuss the weak head normal form. Interesting reading: en.wikipedia.org/wiki/Graph_reduction en.wikibooks.org/wiki/Haskell/Graph_reduction en.wikipedia.org/wiki/Lambda_calculus_definition Timestamps: 00:00 - Reductions 03:44 - Sharing 05:10 - Graph Reduction 05:58 - Normal Form 07:46 - Weak Head Normal Form 13:43 - Demonstration Support me on Ko-fi: ko-fi.com/phag...
Haskell for Imperative Programmers #30 - Software Transactional Memory (STM)
Просмотров 9 тыс.4 года назад
In this video we will explore software transactional memory within Haskell. Example: gist.github.com/phagenlocher/353b24d969598198ca009e055eb2482d Documentation: hackage.haskell.org/package/stm-2.5.0.0/docs/Control-Concurrent-STM.html Paper: www.microsoft.com/en-us/research/wp-content/uploads/2005/01/2005-ppopp-composable.pdf?from=http://research.microsoft.com/en-us/um/people/simonpj/papers/stm...
Haskell for Imperative Programmers #29 - Semaphores (QSem, QSemN)
Просмотров 6 тыс.4 года назад
Haskell for Imperative Programmers #29 - Semaphores (QSem, QSemN)
Haskell for Imperative Programmers #28 - Concurrency & Threads
Просмотров 15 тыс.4 года назад
Haskell for Imperative Programmers #28 - Concurrency & Threads
Haskell for Imperative Programmers #27 - Exceptions
Просмотров 7 тыс.4 года назад
Haskell for Imperative Programmers #27 - Exceptions
Haskell for Imperative Programmers #26 - Strictness, Thunks & seq
Просмотров 8 тыс.4 года назад
Haskell for Imperative Programmers #26 - Strictness, Thunks & seq
Haskell for Imperative Programmers #25 - Compiling Binaries
Просмотров 7 тыс.4 года назад
Haskell for Imperative Programmers #25 - Compiling Binaries
Haskell for Imperative Programmers #24 - Environment
Просмотров 8 тыс.4 года назад
Haskell for Imperative Programmers #24 - Environment
Haskell for Imperative Programmers #23 - Modules
Просмотров 10 тыс.4 года назад
Haskell for Imperative Programmers #23 - Modules
Haskell for Imperative Programmers #22 - Either
Просмотров 12 тыс.4 года назад
Haskell for Imperative Programmers #22 - Either
Haskell for Imperative Programmers #21 - data, type & newtype
Просмотров 17 тыс.4 года назад
Haskell for Imperative Programmers #21 - data, type & newtype
Haskell for Imperative Programmers #20 - Advanced Exercises
Просмотров 15 тыс.4 года назад
Haskell for Imperative Programmers #20 - Advanced Exercises
Haskell for Imperative Programmers #19 - Infinite Lists
Просмотров 13 тыс.4 года назад
Haskell for Imperative Programmers #19 - Infinite Lists
Haskell for Imperative Programmers #18 - QuickCheck
Просмотров 24 тыс.4 года назад
Haskell for Imperative Programmers #18 - QuickCheck
Haskell for Imperative Programmers #17 - Monads
Просмотров 63 тыс.4 года назад
Haskell for Imperative Programmers #17 - Monads
Haskell for Imperative Programmers #16 - Type inference
Просмотров 25 тыс.4 года назад
Haskell for Imperative Programmers #16 - Type inference
Haskell for Imperative Programmers #15 - IO
Просмотров 38 тыс.4 года назад
Haskell for Imperative Programmers #15 - IO
Monadd was definitely worth it lol, these videos have all been really good so thank you!
First time actually learning Haskell, sadly couldnt find a way on my own to filter the edges and avoid infinite loops in my attempt at Exercise #4, but honestly kinda of proud of what i've achieved. Very much voodoo, but hopefully I can come back here eventually and laugh at this implementation, lmao hasPath :: [(Int, Int)] -> Int -> Int -> Bool hasPath list startNode endNode | null list = False | startNode == endNode = True | otherwise = hasPath list (snd (head [(x,y) | (x,y)<-list, (a,b)<-[(a,b) | (a,b) <- list, startNode == a], b == x])) endNode
Here is something mind-blowing for you. Foldr is not tail recursive, and we therefore want to use foldl instead to prevent stack overflow, which is tail recursive. Here is what people tend to think: "as long as the fold function is commutative, i can use foldl and foldr interchangeably, and if not i can just swap the arguments". The reality is that you also need the fold function to be *associative*, and if it isn't, there is generally no way to make a foldr into a foldl, and no way to make it tail-recursive, even if you implement it yourself.
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
0:41 types, evaluation haskell toolchain ghc ghci cabal
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
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
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'
This was awesome and very helpful.
"You can do this just as well imperatively, but that's not the point" seems to be Haskells mantra 😂
I did: getWords xs = do a <- getLine if null a then return xs else do getWords (a:xs) And called it with: getWords []
Take my value. Please. 😉
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
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.
Haskell is lazy, That just like us programmers, that's why we love haskell!
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
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)
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) ```
this course is just great
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)
Took a few watches to get this one down lol
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!
Thank you for this very useful video!
5:45 moan add
Exercise 4 made it an imperative to not use Haskell...
fst = first; snd = second. took me a mo...
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.
great videos!
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.
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: ruclips.net/video/7SidSvJcPd0/видео.html Perhaps Haskell could evolve in that direction too?
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?
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.
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)
This is great. It's the first course I've found that actually compares Haskell to what I already know. Programmers everywhere thank you.
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).
I wonder where all the discarded thunks end up 😢
🔥thank you for the videos
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
Exercise 2 is the prefect argument against functional programming.
prefixes = [take m n | m <- [1..length n]] Nice, simple, elegant.
@@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
@@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.
This video ain't it. I understood virtually nothing about data types or their uses.
Skill issue
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.
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.
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
Great video series. Was there a conscious choice not to use function composition?
Yeah, I give up. This is not understandable at all
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.
Ex2 doesn't remove duplicates.
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
very hard... lol....forgot recursion since 1997... need recycle my brain
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