1 Problem, 16 Programming Languages (C++ vs Rust vs Haskell vs Python vs APL...)
Ғылым және технология
A video taking a look at 16 programming language solutions (C++, Rust, D, Clojure, Ruby, Elixir, Raku [Perl 6], Haskell, Racket, Julia, Python, APL, J, BQN, Pharo Smalltalk & Fortran) to one problem.
ADSP Podcast: adspthepodcast.com/
Array Cast: www.arraycast.com/
Contest: 255
Problem Name: Find GCD of Array
Problem Link: leetcode.com/problems/find-gr...
Problem Type: Greedy
Github Links / Chapters:
0:00 Intro
1:47 Problem Statement
2:43 Python: github.com/codereport/LeetCod...
3:20 Julia: github.com/codereport/LeetCod...
3:50 Raku: github.com/codereport/LeetCod...
4:36 Ruby: github.com/codereport/LeetCod...
5:00 Elixir: github.com/codereport/LeetCod...
5:34 Haskell: github.com/codereport/LeetCod...
6:28 Racket: github.com/codereport/LeetCod...
6:51 Clojure: github.com/codereport/LeetCod...
7:30 C++: github.com/codereport/LeetCod...
9:15 D: github.com/codereport/LeetCod...
9:35 Rust: github.com/codereport/LeetCod...
10:18 APL: github.com/codereport/LeetCod...
11:23 Fork / S' Combinator / Starling' / Phoenix
14:18 J: github.com/codereport/LeetCod...
15:11 BQN: github.com/codereport/LeetCod...
16:52 Pharo: github.com/codereport/LeetCod...
17:32 Fortran: github.com/codereport/LeetCod...
20:19 Languages not covered
21:30 Rankings
26:00 Summary
26:56 Podcasts and Outro
Follow me on Github: github.com/codereport
Follow me on Twitter: / code_report
Follow me on LinkedIn: / codereport
Пікірлер: 466
In 30 years programming professionally I have never used gcd in any production code, ever. But good to know it's in the global namespace in so many of these languages.
@Levy957
2 жыл бұрын
Bcz u need a very good reason to use gcd in any code thats not math research related
@RobBCactive
2 жыл бұрын
Application specific features like that SHOULD NOT IMO clutter up the core language. It should provide the features to use well optimised maths libraries. In a game a function sin() might be about players behaving badly rather than mathematical. Reserving names is evil.
@CarrotCakeMake
2 жыл бұрын
Well let me explain why. There is only 1 possible way to implement gcd, no one will ever make a better version, and there is only 1 type that gcd can ever be defined for, and the acronym gcd can't possibly ever refer to anything else. That's why we need our betters to put it into the global namespace.
@PiroXiline
2 жыл бұрын
@SixTB "Blah blahblah" was speaking sarcastically. And he is right. There can be only one type to do `gcd` on.
@PiroXiline
2 жыл бұрын
@SixTB Ok, you're both trolling, I'm also.
I find it interesting that Rust is the only language where you have to explicitly say that this function will crash if you pass in an empty list, I like that.
@angeldude101
2 жыл бұрын
It's a bit verbose, (and the namespacing should've used `use num::integer::gcd;` on an earlier line) but I absolutely love that Rust forces you to consider the edge cases like that. If any other function gets given an empty list, either they'll just crash unconditionally, or give some kind of weird default value (I think). Rust actually just says "I don't know if the minimum or maximum exist at all so you have to deal with it before you can work with a value that may or may not exist." My solution would probably be: pub fn find_gcd(nums: &[i32]) -> Option { use num::Integer; nums.iter().min()?.gcd(nums.iter().max()?) // Or maybe // Integer::gcd(nums.iter().min()?, nums.iter().max()?) }
@liesdamnlies3372
2 жыл бұрын
Easily one of the most attractive things about Rust. It’s got speed, but instead of letting you shoot yourself in the foot, it grabs your hand and says “stop it, get some help.” Edit: If you absolutely need even more speed, that’s what FFIs are for. But then your thinking will shift into “this is danger zone land, better be careful.” Other programming languages expect you to just stop being a dumb human. Well, humans are remarkable flawed. Thirty years of constant security nightmares, so often caused by simply mismanaging strings and their pointers, has shown us that.
@nilstrieb
2 жыл бұрын
@@liesdamnlies3372 If you absolutely need more speed you can use unsafe, you don't need FFI.
@liesdamnlies3372
2 жыл бұрын
@@nilstrieb You have no idea how hard I just facepalmed at myself just now…dammit…
@clyde34
2 жыл бұрын
@@angeldude101 I'd go with the variant you commented out, since chaining binary mathematical operators becomes really hard to read. Also, cramming everything into one line is unnecesary. It's much more readable when you split it into logical units pub fn find_gcd(nums: &[i32]) -> Option { Some(num::integer::gcd( nums.iter().min()?, nums.iter().max()?, )) }
4:36 Ruby is expression based, the last line will be implicitly returned if you leave off 'return'
@code_report
2 жыл бұрын
Thanks! I didn't know this!
@jaysistar2711
2 жыл бұрын
@@code_report Ruby also influenced Rust's Lambda expression and return syntax.
@rustycherkas8229
2 жыл бұрын
I don't know Ruby, but the word "implicitly" in your comment reminds me of "The Elements of Programming Style" A Fortran programmer, years ago, wrote 4 lines of comment to document that omitting "END" after the final subroutine was fine because the compiler would see the EOF... Laughable... The demonstration that all of these languages each have their subtle idiosyncrasies to trip-up newbies is terrifying... "Back in my day," said the old man, "when we needed gcd() we spent 10 minutes coding it, not 2 hours lamenting that the language, unlike some others, didn't have a built-in." This is the modern implementation of "Jack of all trades (languages); master of none."
@VivekYadav-ds8oz
2 жыл бұрын
@@rustycherkas8229 Not really. It's an immediate compiler error because if you forget to return, or put the semi-colon after the last statement (atleast in Rust where the expression must be naked), or put a wrong statement as return, it'll be a type-mismatch. Never encountered such a bug thanks to static typing :)
@rustycherkas8229
2 жыл бұрын
@@VivekYadav-ds8oz Dunno... Maybe I'm old... I remember the time before "lint"... The good ol' days o' the 'C' Wild West, where "men were real men!" (and the man page for Unix 'cat' was only a few lines!) To these old eyes, "One problem; 16 languages (sorry if your favourite was excluded)" rings of "The Tower of Babel"... (Moore's Law applied to language population; doubling every 18 months...) Once upon a time, the title "Craftsman" was earned by learning and applying ALL the nuances of one's craft... I stand by the expression, "Jack of all languages; master of none."
The Rust code surprised me a bit, because: - The Rust stdlib doesn't have a gcd function, nor a `integers` submodule - this is part of the `num` crate instead, which is an external dependency but has the added benefit that it makes the calculation generic. - passing the whole Vec by value is inefficient and leads to the ceremony that you have there on top of being an obvious performance issue - instead, accepting a slice reference allows you to get to `.min()` and `.max()` directly. - Rust is a safe language and incentives keeping safety. Making the return value optional leads to a cleaner solution. All in all, the better solution (with the num crate) is this: fn find_gcd(arr: &[i32]) -> Option { Some(num::integers::gcd(arr.min()?, arr.max()?)) }
@halbgefressen9768
2 жыл бұрын
Do they have ? for Option now? Nice!
@arjentix
2 жыл бұрын
It looks a little bit inefficient cause it takes two iteration over arr to get min and max, unlike c++ version with min_max() function. Is there some good alternative of this in Rust?
@zyansheep
2 жыл бұрын
@@arjentix you would probably have to either write your own .fold() accumulator lambda, or use a library
@VivekYadav-ds8oz
2 жыл бұрын
Vec is moved, not copied. Not sure 8*3 = 24 bytes of pointer + data + capacity is much of a performance issue. But I do agree that you should take a slice whenever possible, as it makes it possible to interop with a lot more contiguous data structures as possible.
@VivekYadav-ds8oz
2 жыл бұрын
@@arjentix You would probably need to write your own function for that. Not a big inconvenience, but it would still be nice to have an inbuilt one, y'know? I'm pretty sure that a single PR or RFC on GitHub and it'll be there in the next stable.
17:33 "And last, but not least, Fortran" Last place in actual rankings: Fortran
@code_report
2 жыл бұрын
Lmao, fair point.
For julia, it is common to use one-line function defs: findgcd(nums) = gcd(min(nums...), max(nums...))
@PanMaciek
2 жыл бұрын
also in julia you don't need to use variable length argument functions min, max, you can use minimum and maximum to find extrema in array this will improve performance
@BoyoWhoGoesPoyo
2 жыл бұрын
@@PanMaciek So: findgcd(nums) = gcd(minimum(nums), maximum(nums))
@martin2sax
2 жыл бұрын
@@BoyoWhoGoesPoyo or even better findgcd(nums) = gcd(extrema(nums)...))
One would almost think there will be a comparison of some legitimate code and not literal one-liners using built-in functions...
@b2gills
2 жыл бұрын
You would think so wouldn't you. The thing is that with the languages like APL most code trends towards being a one-liner. Raku also trends towards simple expressions. Partly because you can modify the language as you go to make things simpler. The languages which are longer have fewer features that allow for this compression of ideas into simple expressions. So they get longer faster.
@rndszrvaltas
2 жыл бұрын
@@b2gills This is just an excuse, let's be honest. The so-called "challenge" was a not terribly useful or realistic, banal task that you could give a newbie after 10 lessons of programming, even without any built-in function. And having built-in functions for not particularly useful functionalities (talking about general purpose languages) is 1. not a statement about the expressivity of the language in my opinion 2. surely not any relevant when one is to judge which language to use for what task or how effective a language is in a real-life scenario. Like what next? Are we going to compare "Hello World" code snippets?
@b2gills
2 жыл бұрын
@@rndszrvaltas I program regularly in Raku, and I can say with confidence that if I were to translate my code to another language it would be orders of magnitude longer. Not to mention the code I write which is not directly translatable. When I write meta code in Raku, that is a feature that is not generally available in other languages. That meta code also allows me to condense and clarify the code further. The languages which are longer in this example tend to also be longer in general. I have seen a lot of code on the codegolf stackexchange. The general size comparisons among the languages tend to be consistent. Actually that's probably a best case scenario for the longer ones. The shorter ones would tend to be shorter anyway. When I actively participated, the Raku code was almost always among the shortest of the non golfing languages. There were quite a few times where my golfed code was what I would have written anyway.
@rndszrvaltas
2 жыл бұрын
@@b2gills I'm not arguing for or against Raku, mind you. Actually, I'm trying to pick up Raku because I like the principles of it and it definitely makes life easier if you need to script Linux. That doesn't change anything related to the actual video, though. It's not informative in any way and no, trivial tasks, let alone solved with library functions, do NOT tell anything about the overall verbosity of a language. It tells exactly nothing about the language - or, if anything, whether it's shaped accordingly to this particular task or not.
@b2gills
2 жыл бұрын
@@rndszrvaltas Raku let's you manage complexity by pushing your complexity into the language. I sometimes write functions which don't have any code in them because the entirety of the complexity needed for the function is handled by just the signature and using multis. Both of which lead to simpler and shorter functions. So this example is actually more exemplary than you might think.
liftM2 takes a function (a -> b -> c) and returns a function (f a -> f b -> f c), (f : Monad) note that you don't need the monad constraint since maximum does not rely on the result of minimum or vice versa - and could just use liftA2 from Control.Applicative - not that it matters in this case but Monad is sequential where as solution using Applicative could be made to be parallell. or: gcd minimum maximum
@w-mwijnja8919
2 жыл бұрын
To be honest, this is the kind of situation where I think that using applicative or monadic abstractions does not make the result more clear (because the applicative/monad instance for functions is one of the harder ones to grasp). I prefer a less feature-heavy solution like ``` findGCD2 nums = gcd (minimum nums) (maximum nums) ``` Of course this is subjective :-).
@Yotanido
2 жыл бұрын
@@w-mwijnja8919 I generally agree, but I think using the applicative operators and is plenty readable. Especially since this sort of pattern comes up a decent amount of time. At least in my code.
@neclitorismagnum
2 жыл бұрын
Monad is not necessarily sequential. Computations that don't depend on each other can be evaluated in arbitrary order.
The real test of a language is how well it can do things that the language designer wasn't thinking about. Arrays of words and gcd are way too common. Something like shortest path on a graph represented as a matrix would be better. Or something that requires less trivial datatypes like rational numbers or large numbers.
@tonyennis1787
2 жыл бұрын
Your first sentence is akin to faulting a screwdriver because it is a lousy hammer.
@CarrotCakeMake
2 жыл бұрын
@@tonyennis1787 No, it is akin to faulting a screwdriver because it doesn't work in cold weather because the screwdriver designer lives in temperate weather.
@MultiMrAsd
2 жыл бұрын
The real test of a language is: can you come back a year after you wrote the function and quickly understand what it does.
@VivekYadav-ds8oz
2 жыл бұрын
Wow.. this puts a lot of my problems with certain languages in one sentence. A truly flexible and ergonomical language will excel even in cases not protected and overseen by the designer. I'm inclined to also think C++, but Rust for sure shines a lot here. Unless you want to go against the borrow checker, in which case, good luck XD
@andrewdunbar828
Жыл бұрын
@@tonyennis1787 Screwdrivers are not Turing complete.
I'd be interested in seeing a Cobol solution in one of those videos. We all hear about how horrible Cobol is, but most of us never seen examples as to why.
@ceber54
2 жыл бұрын
I'd tried to learn Cobol once learning from an IBM course, but essentially i learnt almost nothing. First, you can't run Cobol locally, only runs on IBM mainframes, so to compile your code you must do it remotely. Is tab sensitive, and that is a not for me. If i remember, there not such thing as a for, only goto, something that i only have seen on assembler. Also the compiling routine is awful, you must check before compiling some generated files to look for errors, and then you can compile. is it readable? yes, but by modern standards over-complicated, from COBOL and FORTRAN, i prefer FORTRAN, seems easier.
@drew570
2 жыл бұрын
COBOL is an absolutely atrocious language. Some problems are borderline impossible to implement in COBOL so the problems will have to be simple.
@watchableraven3517
2 жыл бұрын
My grandma used to work with COBOL. A procedural oriented language for business. Very cool to hear about it
@r.pizzamonkey7379
2 жыл бұрын
@@ceber54 Readable? I guess we have different definitions of readable. Compared to Assembly sure, but honestly I find C considerably more readable.
@ceber54
2 жыл бұрын
@@r.pizzamonkey7379 The argument of why the sintaxis of cobol is how it is, is because the code should be audited by someone not expert in the code, or at least that was the initial idea. That what it means when "cobolist" refer Cobol as readeable. But yes C, or even Fortran, is easiest to understand at first glance than Cobol. Personally I don't think that cobol is readeable.
"Languages should have GCD in the core library" - why? How often does the average programmer use this? Fortran is great for programming array programs across multiple cores and servers - Julia is the modern alternative.
@fburton8
2 жыл бұрын
Good question. I'd love to see some examples of gcd use in real life problem solving.
@hemangandhi4596
2 жыл бұрын
That too, then he docks BQL for this despite it decently explaining why it doesn't have a GCD built-in.
@ZReChannel
2 жыл бұрын
Maybe once every ten years
@hanyanglee9018
2 жыл бұрын
@@ZReChannel I guess this is the reason that I started learning programming in 2015 and don't know what GCD is today.
@sebastienleblanc8613
2 жыл бұрын
When you have to display fractions to a user, you can easily reduce a fraction using GCD. 14/21 reduces to 2/3 because (gcd 14 21) = 7, so (14/7)/(21/7) → 2/3. Not finding a use for something does not make it inherently useless.
I'm so glad you were a good sport and gave Fortran a go, it gives a nice contrast to the other languages.
At this point I think you are cherry picking these challenges so they are elegantly implementable in apl xD I would love to see a medium level leetcode exercise (even if its fewer languages then)
You can simplify the C++20 a tiny bit further by using "minmax" instead of "minmax_element", which directly returns the two values instead of their iterators, sparing you from doing the dereferencing. The old non-ranges minmax only works with two values (or an initializer list) unfortunately, so this only work with C++20 ranges.
@VamoAComeLaPapa
2 жыл бұрын
std::minmax receives just 2 elements. Here you need to receive a sequence of elements.
@Possseidon
2 жыл бұрын
@@VamoAComeLaPapa I was talking about C++20 std::ranges::minmax, which does take a whole range. I even mentioned your very same issues with the old std::minmax in the final sentence? :)
@VamoAComeLaPapa
2 жыл бұрын
@@Possseidon yes, you are right, it seems that one of the overloads of std::ranges::minmax is an alias for std::ranges::minmax_element Thanks!
Why Fortran is so popular? Because it is still a thing if you are number crunching :-) using Fortran with OpenMP and MPI on big clusters is a thing e.g. for climate models, aerodynamics, optimization, and other science and engineering stuff. And wrt your question of how to pass an array without passing a the size first: Assumed shape arrays Declare it as: INTEGER, DIMENSION(:) :: nums Like this the subroutine expects a 1-dimensional array which has been allocated before. For 2D it would be DIMENSION(:,:) etc. Btw: the IMPLICIT NONE would usually be put on top of you MODULE and thus you do not repeat it in all functions or subroutines.
@code_report
2 жыл бұрын
Honestly, the responses from the Fortran community here and on Twitter have been amazing! I am definitely going to be doing a follow up video.
@pavelperina7629
2 жыл бұрын
I'm quite surprised, I expected it's dead for 30 years and replaced by Pascal, C, .... Last 15 years Python is extremely popular, but it's painfully slow. C++ misses any useful standard libraries for image processing, worker thread pools, parsing even most basic files (ini, csv, xml, ...) and standard template library is like 100x slower in debug mode (at least one that comes with Microsoft Visual Studio and contains extra error checking). It's quite painful to setup build environment for extra libraries. I've just discovered Julia week ago and I think I may use it for some projects (already wrote 600 lines of data crunching code and it seems ok)
@ingenium5831
2 жыл бұрын
@@pavelperina7629 30yrs ago, the fortran90 standard has been released which has made a huge step from F77 (which I have never used, I have only to understand it sometimes when debugging in the ancient parts of our software :-D computed GOTO... arg). And if you check modern standards like 2003 or 2008 which introduced object oriented features like inheritance etc, or native parallel stuff like DO CONCURRENT, you have a modern language (the OOP stuff feels a bit cumbersome to be used, but it works fine). I think it is a fine language for the number crunching work, but I'm no IT guy, but a mechanical engineer who codes 🙂
@astroid-ws4py
2 жыл бұрын
@@pavelperina7629 Fortran is still well alive, Soon an official LLVM Fortran compiler called Flang will be released, and an interactive LFortran compiler is currently in the works, And also there are high quality Fortran compiler from Intel and Nvidia called Ifort and NVFortran respectively, So it is well alive, But I guess they need to do some updates to the standard, Maybe add generics, templates, meta-programming, tuples, lambdas to the language, After that it could be a fun language to work with which could be substituted for the likes of C++ or Rust for certain systems level programming tasks
@kekaci
2 жыл бұрын
@@ingenium5831 I've used f77, and even f60. They're fun, but pretty unusable. f90+ is definitely the best experience
@3:26 A better Julia solution IMHO is find_gcd(nums) = gcd(extrema(nums)...) NB: The splat operator (...) goes after the call to extrema, not on the nums array itself.
@red13emerald
2 жыл бұрын
Julia is such a beautiful language, this is incredibly easy to read and terse as hell. I’m reading through the docs right now, can’t wait to find a project I can actually use it on.
@oxey_
2 жыл бұрын
that's cool
@brettknoss486
2 жыл бұрын
I'm not sure if the method is needed. If nums is already defined then it isn't. This video is misleading, if it doesn't show how a function is called, or how the array is defined.
It's nice to learn what it looks like to program the same simple problem using different languages, although I really don't think it's necessary to put a ranking part in the video, I've no idea what that part is for.
Your opinion of each language and its solution seems to be based entirely on brevity and elegance. If I saw any of these solutions in a production code review I would ask for them to be heavily commented to explain their function and to aid readability. There is nothing wrong with judging a language subjectively but it would be helpful to explain your criteria for judgement somewhere in the video.
@xGOKOPx
2 жыл бұрын
If you think that these solutions have to be heavily commented then I don't think the solutions are the problem here
@sunglow9835
Жыл бұрын
you should look at 1:24 where he says he is ranking these "from favorite to least favorite"
@JackDespero
Жыл бұрын
@@xGOKOPx If someone sent me any of the array language solutions, I would automatically reject them and answer that a cat run over their keyboards, apparently.
@xGOKOPx
Жыл бұрын
@@JackDespero I was talking about the solutions from more conventional languages here. They're all pretty readable if you have basic familiarity with the concepts (although C++ namespaces do create some visual noise). And if you were in a position where someone sends you a solution written in an array language and you can accept or reject it, then you'd probably know that language, so it would be readable to you as well
@qwerte6948
Жыл бұрын
@@xGOKOPx dude like youll be able to read last few ones 💀 personaly it doesnt matter the language if your writing code in rust, C or phyton becouse theyll just get compiled to binary anyways... simply readable easy language > cryptic language especially if your working with a team of people
As a python programmer, I really liked Ruby's syntax.
you can use the java interop with clojure and remove the import (defn find-gcd [nums] (.gcd (biginteger (apply min nums)) (biginteger (apply max nums)))) but tbh this problem is trivial, judging a language on the fact that it doesn't have gcd in its stdlib and "too many parens" is a bit silly. also the above solution took me 20 seconds of experimenting in the REPL. The haskell solution was beautifully elegant, APL was a bit too cryptic for me.
@jonseltzer321
2 жыл бұрын
I would add that Clojure benefits from the JDK ecosystem - the largest library ecosystem of all 16 of these languages
@andrewlalis
2 жыл бұрын
@@jonseltzer321 this is why it's been hard for me as a Java developer to transition to D and other small languages that have a better design, but a much smaller ecosystem
@jonseltzer321
2 жыл бұрын
@@andrewlalis Agree, that's why I don't understand why you don't have a 'Haskell for the JVM', or 'APL for the JVM'.
@VivekYadav-ds8oz
2 жыл бұрын
Damn Clojure was pretty smart to keep backwards compatibility in such a simple way. No extern "Java" crap, just simple 'yeah just name the library. meh.'
I really like your videos, I learned of APL's existence from content you create (here and the podcasts). But in most of the examples you choose, APL seems to excel because they're usually in the domain area that fits APL/J/BQN: transforming a list, or reducing it to a response. I'd like to see you address the other side. Problems for which APL is not a good choice.
@MrAbrazildo
Жыл бұрын
This comment should be pinned.
I know people from the department of theoretical solid body physics that use Fortran a lot. They have all their packages written in it and they need it for the performance to simulate just a few % more atoms at the same time.
I love that Smalltalk is added to the mix! Please consider Idris in the future too.
Your idea of a low-level language is different to mine. I used 360/370 assembler in a 30 year coding career 😊 PS Love the array cast podcast
Not sure if it has been mentioned already, but in the Julia version, you want to use the minimum function instead of min for arrays. Or even better here, the extrema function to give you both minimum and maximum. ```julia gcp(extrema(vector)...) ```
@tubesteaknyouri
Жыл бұрын
I was about to make the same comment.
Me when looking at APL: Where are those keys on my keyboard?
@oxey_
2 жыл бұрын
yeah those languages are completely unreadable and seer really hard to write. I know nothing about them, perhaps they're great. On first glance though I thought I was looking at brainfuck lol
@kruksog
2 жыл бұрын
@@oxey_ if you have a solid understanding of mathematics and lambda calculus, it's not bad at all.
For part 2, we need SQL or PL/SQL, some version of Assembly, Bash, Prolog, Scratch, Piet and Cobol!
This is extremely nit-picky but for your python solution nums should be type annotated with list[int], not List[int]. PEP 585 makes it so the lowercase version is the generic and List, Tuple, etc are going to be deprecated
@m.sierra5258
2 жыл бұрын
Same for rust, Vec[u32] should be &[u32]
Clojure could also just call out to Java: (defn gcd [a b] (.gcd (biginteger a) (biginteger b)))
People that requested Fortran wanted to see you suffer :D
@cobaltno51
2 жыл бұрын
well, they could have just asked for x86 assembler
@Acaykath
2 жыл бұрын
@@cobaltno51 Or code it purely in nand based boolean logic for real bare-metal level programming.
@anton7354
2 жыл бұрын
@@cobaltno51 Ever heard of ARM SVE2? Or Itanium for that matter? :-D
great video, thanks for the huge effort putting this together! at the end comparison part it would be nice to see the code popping up when you select the languages. also i'd be interested in runtime comparison as well :D
This channel is pure gold : )
7:05 Namespace qualified symbols in Clojure use a forward slash as the separator, which can also be used for accessing static members such as (Math/min 1 2) or Math/PI. The standard dot is used in non-static method/constructor host interop (although (. Math PI) would also work), where the syntax is also a bit different: (.toString (.append (StringBuilder.) "foo")) or (.. (StringBuilder.) (append "foo") toString) with the double-dot macro which both expand to (. (. (new StringBuilder) append "foo") toString) anyway.
Years ago I use to think on Rust just as a "another programming language, such as many others". Nowadays I'm surprised by the evolution either as the language itself and their community, as well as their applications. Unquestionably has a long future for embedded systems, kernel source code (+6.* Linux kernel is a clear example of this), general purpose development, even for networking APIs, and basically an interesting replacement for C++ and Java for OOP. Rust is the present but it's the future too, I trust with absolute hype this can be useful as C language and other classic languages, basically it's a swiss army knife.
I kinda wanted to see you implement your own solution in all the languages but sure, you could do this too
It's been a long time since I've done any Fortran, but from memory you declare an array argument using the syntax nums(:) (or my_matrix(:, :) for a rank-2 array etc). Also, you don't need to explicitly name the output variable for a function -- it defaults to the function name, so you can say integer function find_gcd(nums) integer, intent(in) :: nums(:) find_gcd = gcd(minval(nums), maxval(nums)) end function find_gcd implicit none can go at the module level so you don't need to say it in every function, or you can use a compiler switch to apply it everywhere (-fimplicit-none or something like that?)
C++ 20's std::ranges has a minmax for value types. Also the returned structs are compatible with structured bindings. int find_gcd(std::vector nums) { const auto [min, max] = std::ranges::minmax(nums); return std::gcd(min, max); }
@petermuller608
2 жыл бұрын
Imo the solution shown was nicer
You don't need the elipses in Julia if you use the functions minimum and maximum instead of min and max
@stillyslalom1
2 жыл бұрын
Better yet, `extrema`, which enables this: `find_gcd(nums) = gcd(extrema(nums)...)`
For Raku, storing a list in a scalar may lead to surprises. Also, gcd can be an infix operator, but doesn't have to. sub find-gcd (*@nums where (@nums.all ~~ Numeric or fail(‚I don't know how to find a gcd for a non-number.‘))) { [gcd] .min, .max with @nums } This reads: force the argument list into a list and check if it contains a number. Return the gcd of the smallest and largest element of that list. Raku is smart enough to deal with lists of strings. So the check against Numeric serves as basic input validation. We would get a not so nice runtime error by min without the check in the signature.
@MogTM
2 жыл бұрын
imo, if you're going to use `with` anyway, you might as well skip the reduction and use &gcd as an infix operator: .min gcd .max with @nums # but TIMTOWTDI, of course
@zengargoyle
2 жыл бұрын
raku -e 'say {[gcd] .minmax}(2..10)' 2 Yeah, we got the good stuff.
@MogTM
2 жыл бұрын
@@zengargoyle oh, interesting - I didn't realize that Range.minmax returns a List (unlike Any.minmax, which returns a Range). So to accept Arrays as input, you need .minmax.bounds (or, I guess, .minmax.minmax - which is a pretty odd construction!) but if we only need to accept a range then your one-liner works. I wonder why Larry et al. decided to have the two .minmax methods return different types?
FORTRAN is a very popular language, because there is honestly no better low level language for scientific programming. You can easily get faster code than C which is easier to work with because the concept of arrays and matrices is native to FORTRAN (FORmula TRANslator), while in C is basically a hack. A large amount of very important code in the scientific world is written in FORTRAN.
Now Rust with generics and you can use any integer type and without unwrap, it is panic-free if the input is empty. Not sure, how the other languages handle this: use itertools::*; use num::*; pub fn find_gcd(nums: T) -> Option { let (min, max) = nums.minmax().into_option()?; Some(max.gcd(&min)) }
@NoNameAtAll2
2 жыл бұрын
does rust not have an unwrap? why can't one pass the '.into_option()?' tuple as 2 parameters of gcd?
Haskell: findGCD = gcd minimum maximum Or giving a maybe in case of empty list: findGCDM = fmap findGCD . nonEmpty () Is the S combinator when applied to functions () is the B combinator when applied to functions. It's fmap in operator form, and (.) With different associativity and precedence.
Fortran is tops for massive parallel super computing and the fortran linear algebra libraries are rock solid and proven bug free for decades so the results can be relied on. (by massive I mean single computations that take $50,000 in electricity for each run.)
Somebody needs to write a book on point-free. Every time I've seen it in Haskell it looked like doing a root canal on yourself without anesthesia. I know it's part of the whole March of Category Theory, but really . . . Yeah, any advice welcome.
@qandak
2 жыл бұрын
There is nothing to do with Category Theory. Only higher order functions with lazy/partial evaluation. You can easily read things like "f = g h" as an aliasing. Everything passed to the "f" (if expected by type) will be passed to the right-hand side :)
@vonBottorff
2 жыл бұрын
@@qandak But isn't "point-free" another name for Category Theory?
@qandak
2 жыл бұрын
@@vonBottorff Nop
@argh44z
2 жыл бұрын
@@vonBottorff No. APL is far more point free than something like haskell, for example. Other way to think about point free is how julia does it (multidispatch), which is also emutable with anything w/ dynamic dispatch with type Any.
@0LoneTech
6 ай бұрын
Point free means you're working on entire spaces and transformations, rather than individual points within them. That usually takes the form of removing arguments that are simply repeated; e.g. addone x = 1+x translates to addone = (1+). x here is a placeholder for one particular point, while its type is the domain the function can operate on. In practice it's just decluttering the data paths, hopefully letting us consider our operations at a higher level. revwords line = let ws = words line rws = map reverse ws result = unwords rws in result revwords line = unwords (map reverse (words line)) revwords = unwords . map reverse . words -- . is the ∘ combinator for unary (aka monadic) function composition, pronounced "of" revwords "olleH !dlrow" This simple example relied on the input argument only occurring once, at the end. If that's not the case we might want to use combinators like liftA2, flip or const (map is a second order combinator, operating across a whole list). Building such combinators is where lambda calculus kicks in, the overly mystified art of variable substitution. The mere fact we can leave arguments out is because we have partial application and first class functions. Point free style in Haskell is like tacit style in APL, but APL has more combinators built into its syntax. Category theory is usable to describe function compositions, but does not on its own relate to source text, which is where style like point free is expressed.
C++ solution is better that the other because the number of comparisons: - C++: ceil(3/2 * n) - 2 - The others: 2n - 2 Thanks to Ira Pohl's algorithm
Definitely inspires me to check out APL/J. I'm a big fan of Rust/Idris but I'd like to branch out a bit more. Would have liked to see Prolog shown off, hopefully we see it more in future videos.
@code_report
2 жыл бұрын
Prolog will definitely get highlighted in the future!
6:51 for the Clojure solution, it might just work if you change the symbol `math.gcd` to `math/gcd`. The slash is how you access vars from a namespace, which is how functions are interned/accessed as well.
I'm interested with programming languages specially their syntax so I really like this series of videos. What I want to see is that compare this languages in a real world programs like a simple game or simple blog website or other type of applications.
Fortran would also be as fast as the C++/Rust solutions (or even faster, sometimes). Being a very old language it has some baggage that can throw people off (including its verboseness, which CS people hate, though scientists tend to like) ..... but I'm also curious about how much time you spent on the Fortran solution. One of Fortran's redeeming qualities is that it's a ridiculously easy language to learn. I've trained many grad students in it, and even those with no programming experience to learn it within a day (not all of it, but most of what they need to write scientific code).
@jmhimara
Жыл бұрын
@Delyan Kirov c++
I think Kotlin is quite clean: The gcd function is a 1-liner and I also have to explicitly say "Crash if the list is empty": fun gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) fun findGcd(nums: IntArray) = gcd(nums.minOrNull()!!, nums.maxOrNull()!!) Alternatively as extension function, also returning "null" instead of crashing if the list is empty: fun Int.gcd(b: Int?): Int? = if (b == 0) this else b?.gcd(this % b) fun findGcd(nums: IntArray) = nums.minOrNull()?.gcd(nums.maxOrNull())
@nighteule
2 жыл бұрын
In the first example, I think you can also make the "gcd" function tail recursive. Doesn't change anything conceptually, but it would make it somewhat faster
A note about the Rust code: declaring a parameter's type as T (as opposed to &T) is a guaranteed move unless T implements the Copy trait. Any subsequent use of the value passed to the function would result in a compile time error. As such &[i32] would've been a more appropriate parameter type (or &[T] where T: Integer + Copy).
@CarrotCakeMake
2 жыл бұрын
Clone instead of Copy would give a solution that works for large number types.
@VivekYadav-ds8oz
2 жыл бұрын
@@CarrotCakeMake finding .max() or .min() don't require any sort of copy, as the internal .cmp() function takes its comparison numbers by reference anyways. &[T] where T: Integer is good enoughl.
@CarrotCakeMake
2 жыл бұрын
@@VivekYadav-ds8oz Ord::max/min returns the same type it outputs, so it either has to clone or return a reference. num::integer::gcd requires ownership of an integer type. So you have to clone sometime, but you could get away with it by doing a gcd(found_min + 0, found_max + 0) maybe. Having gcd require ownership seems like a mistake unless there is a really good optimization that comes from it.
Ngl I expected Fortran to look much more arcane like COBOL. It's surprisingly familiar
The point of FORTRAN is that specific declaration of variables reduces coding errors.
@tonyennis1787
2 жыл бұрын
ding ding ding a winner. Compile-time errors are *much* cheaper than run-time errors.
Very cool video!
I don't program in array languages but from what I've seen I think I'd pick J over the rest because of the ascii syntax.
The algorithm for GCD is actually beautiful and it's a shame you didn't implement it yourself. Here's my version in Kotlin: fun gcd(a: Int, b: Int): Int = when (val r = a % b) { 0 -> b else -> gcd(b, r) } which makes my solution to this problem: fun findGCD(nums: Array): Int = gcd(nums.max(), nums.min())
When I saw "array", "gcd", and "APL" together, I knew how hilariously short it would be
// Here is our C# version: With static local functions using expression syntax. static int FindGcd(int[] nums) => Gcd(nums.Aggregate((prevNum, num) => Math.Min(prevNum, num)), nums.Aggregate((prevNum, num) => Math.Max(prevNum, num))); // Self-written Gcd function, but very short and simple: static int Gcd(int a, int b) => b == 0 ? a : Gcd(b, a % b);
The APL type languages feel like they exist only for toy examples of elegant maths. I can't imagine the pain of having to implement or support anything beyond these barest minimal examples in the real world. It's lie someone saw regex and decided everything should be as difficult to read, but also lets make everything a special character.
Very nice! I thought you were going to write the gcd function, but it would be too long ... Maybe you could write for the 5 languages you like most. =) The C++ single pass is neat, and maybe it deserved a better rank because of that. :)
@petermuller608
2 жыл бұрын
To be honest, most other languages have a minmax function, too ;)
@landsgevaer
2 жыл бұрын
The gcd part can be done recursively quite easily using pseudocode like gcd(x,y) = (if y == 0: x, else: gcd(y, x mod y)) I would also have preferred finding the gcd of *all* numbers in the array, instead of the min&max. To be honest, the way it is now, the video more or less boils down to "which language has the most succinct gcd and minmax syntax". And I hate it when programmers' kneejerk reaction is to require builtins for even simple tasks.
@VivekYadav-ds8oz
2 жыл бұрын
It would, if that was something truly revolutionary or hard to implement yourself.
The transition between C++17 and C++20 was really wack (look at that `nums` go!) Was there any way to adjust that by hand? What do you use for those?
I was sure you'd put Fortran in the first place for lulz
I would be very much interested in which languages have a specialized `minmax` function to calculate both the minimum and the maximum in a single-pass, which languages ('s compilers) are smart enough to optimize separate calls to `minimum` and `maximum` into a single pass regardless, and in which languages you will be stuck with a solution having multiple passes over the collection. The results probably are not that useful for practical programs (as this is a micro-problem of course), but I'm still curious :-).
@ralphmellor1909
2 жыл бұрын
There's a comment above showing Ruby and Raku using an explicit `minmax`.
I might be wrong, but julia has the minimum and maximum functions too, that way you don't need the elipsis.
The problem with fortran is that you have to dive deeper to enjoy it's power. Custom operators it's property what's people though when they recommended Fortran. But to get there, there's still some water to go above your head.
Decoding the smalltalk version: self corresponds to the array self min -- asks self for its minimum, returning a number self min gcd: ... -- takes the result of self min and sends it the gcd: message self max -- sends the max message to self gcd: self max -- this is the message send to self min in a more conventional OO language it would look like self.min.gcd(self.max) which looks remarkably like the Ruby version. Importantly, gcd: is _not_ any kind of infix operator in Smalltalk.
ok, APL style languages look intentionally confusing, can you imagine doing anything more then just simple programming tasks with them? These languages are like fine art, they look cool but don't really do much else
4:52 -> It's interesting to see you talk about the implicit return in Raku, and then not use it in Ruby. you could have written ```rb def find_gcd(nums) nums.max.gcd(nums.min) end ``` or even ```rb def find_gcd nums nums.max.gcd nums.min end ``` if you don't like parenthesis (although, personally, I would keep them in)
Rust is the only language that forces you to deal with the possibility of min/max being called on an empty list. So strictly speaking it's the only correct one. It's not ceremony, the others are wrong.
@cobaltno51
2 жыл бұрын
imagine forcing programmers to write error-free code instead of just segfaulting in production like a real programming language
I don't understand why you would like any language that a) uses characters not found on a keyboard and b) is not human-readable The quality of a language is not defined by how few characters you need to type, but by how easy it is to get the language to do what you want. If I can't read it, and I can't type it, I certainly can't use it.
@lukes5631
2 жыл бұрын
writing code in the future is going to look very different. "type" won't be how you write it.
@klittlet
2 жыл бұрын
There are this things called a) "keybindings" and b) "brain" that are required to use languages like that. APL was lauched in 1966 and has been used ever since by companies like IBM, and influenced many other languages such as C++, Python and Wolfram. Also "human-readable" is a buzz word at this point, if people where capable of using punch cards to program, so is anyone capable of learning a few difficult symbols (if they need-to/want-to of course 😉 ). Should just replace "human-redable" with "lazy-readable". Well explained comments is where it's at.
@Acaykath
2 жыл бұрын
@@klittlet Just because we are capable of something, doesn't mean it's a good way to do it. That's why we no longer use punch cards. We are also capable of programming in binary, and no sane person would go through the effort. You also know as well as I do that most people are too lazy to put in decent comments. If you are tasked with updating something and you need a language reference to look up every 3rd character, then there is something wrong with the language. You should be able to intuitively understand the language by reading it. Imagine telling someone you can read english, so they provide you with something in english but it is written with egyptian hieroglyphs. It's frankly a useless extra step that just makes things harder for you with no gain.
@klittlet
2 жыл бұрын
@@Acaykath lol then you agree with me with the "lazy-redable" bit. Not commenting your code is a bad practice and just exposes a bad programer, also all the majority of applications for back-end jobs that I've seen it was in the requirements to make well documented and commented code. If 'professionals' are not doing this then there's something seriously wrong with this industry. And my last point is: you learned the alphabet didn't you? What commas and exclamation marks mean and when they should be used, right? You've probably learned what && and || mean by association with words, right? Or better yet, the basic logic symbols: "v ^ ¬ -> ≡" that the first semester of college teaches (idk about the US). APL has these same associations. So no, me, the thousands of other people and professionals don't have to look up every 3rd character because we have the basic human function to remember what those characters mean. But I guess we are all wrong then! After all what defines the quality of a language (according to you) is not characteristics like it's runtime performance, rapid prototyping or efficiency, but JUST it's "readability". I guess what you mean by readability is if the ops team can read it or not, which is fair.
@Acaykath
2 жыл бұрын
@@klittlet I actually never learned this in school. Edit: I learned this but with different symbols. v : no idea. Never used or seen a v as anything other than a speed unit. Hmm google tells me this is really an || operator. ^ : I assume this is meant for exponents as that is how it's represented in text equations without fancy math display software. Hmm google tells me this is really an && operator. ¬ : I've seen this symbol before, but outside of ascii art I have no idea what it means. Ahh, google tells me it's a ! (not operator). ->: I've seen this used in some languages as referencing or accessing properties. But I doubt that's what this is meant in this context. The only time I've used arrows in math is to describe limits. Perhaps it is used to apply something to an entire array? I only assume that because apl has array in it's name. Or maybe appends to an array.... this seems to be if !(a && !b) ... Not really sure where this would ever be used... : A double ended arrow. No idea. Perhaps combining a pair of arrays? Hmmm... Google says ((a)==(b)) ≡: Some form of equality operator? See if all contents match? Hmm googling tells me it means having the same value, like a == instead of a === in some languages. in fact, ≡ seems to be the same as . These are completely different from the logic symbols I learned. If I was focused on making graphic card firmware, I might have more of an idea of what the symbols mean. But I prefer working on front end stuff. I know this makes me a little biased. Runtime performance? That's a compiler issue, unless you're doing bare metal operations. Rapid prototyping? If you can't easily read or write it, then everything will be slow, not just prototyping. Efficiency? That's a measure of how effective a programmer is, not the language. Readability? Everything flows from this. A language with good readability makes everything easier, faster, and less error-prone.
In Julia there are maximum() and minimum() too that get a collection, thus no need for the splat operator. And extrema() also exists, thus C++ is not the only lang. that can return both the smallest and largest elements.
@code_report
2 жыл бұрын
Yep, I cover that in a follow up video.
In z^3, x=>GCD(MIN(x),MAX(x)) x can be an array of any shape. z^3 is Array based like APL.
[7:49] julia also has minmax function it's called extrema
@b2gills
2 жыл бұрын
So too does Raku, only its spelled more guessably as minmax.
Might it be possible in Future to compare also the speed of the languages? :D I'm familiar with C++ and know it's fast, when you know what you do. But I'm interested in how much faster it is, or might it be in some circumstances be slower?
Seams like a lot of features have been added to C++ recently. Any recommendations for someone who wants to get familiar with them?
@pavelperina7629
2 жыл бұрын
Lol, I guess you can't. There's too many things in C++ so you can't know all of them. On the other hand very basic things are missing. I'd like to know how to upper-case unicode string, how to sort strings according to locale etc, thread pools,... What I would recommend is either fmt library (3rd party) or std::format (most used functions of fmt made it into c++20) because and are pain.
@MaxHaydenChiz
2 жыл бұрын
"A Tour of C++ (second edition)" seems to be your best bet. Lots of the stuff you are seeing has been available for a long time via libraries (Boost, various proposed extensions, etc.), but they are adding them to the language and removing a lot of the ceremony around using them which is nice. The language updates every 3 years, and you can use the "beta" versions of stuff earlier than that. So, keeping current is a lot.
@hmm7458
2 жыл бұрын
bruh believe me.. it would be better if you don't go down the rabbit hole of C++ it have no ending this language have so much that even pros don't know everything of it
@az-kalaak6215
Жыл бұрын
read the documentation, code side-projects and do not use last cpp version as compilers are inconsistant in their implementation (cpp20 is currently not fully implemented in clang and gcc).
Very good!
It seems to me that the Raku solution is essentially the same as the APL, J, BQN, and Pharo solutions, the commonality being GCD being an infix operation which permits expressing the solution in a Fork idiom.
the auto-generated CC subtitle was wrong, it translated English into Vietnamese from audio directly.
You can simplify the Ruby solution by removing the explicit "return."
What was that python solution? Arrows? Self in a function outside a class? Can someone pls explain? Ty
Infix functions like the example in perl are realy nice to type. thats cool
I'm part sad that 1st place wasn't Prolog, but also glad that D got in.
No GCD in Fortran or other languages? I suppose one can reuse the explanation given for the BQN. About Fortran, you don't really need a module: you can put functions and procedures in "contains": program whatever !.... the program using gcd... contains function gcd(m, n) result (answer) ! ... end function gcd end program whatever and of course ... better using always "==" rather than mixing the old .eq. with ==
I'm also loving Ruby a lot these days, and finding that my Ruby code is strangely functional, even if the "theory" of Ruby is almost purely OOP.
@FranklinYu
2 жыл бұрын
I would claim that Ruby has some FP hidden behind the OO curtain. For example, an FP way might be “filter(map(arr, func1), func2)” (like Python), while a Ruby way might be more like “arr.map(:func1).filter(:func2)” (or even “arr.map { |x| x.getY }.select { |x| x > 1 }” if you need lambda). It looks OO, but the idea is actually pretty FP.
You could have added type signature in haskell that would be pretty general since every function requires only Ord a, (yes, including gcd)
Damn, would have loved to see assembly 😁
In the c++ 20 solution I would like to point out that, probably this : Return std::gcd(r->min, r->Mac) Is more clean than using the start to dereference
Dart (If Java was created in this decade) int findGcd(List nums) { // Sort ascending nums.sort(); final first = nums.first; final last = nums.last; return first.gcd(last); }
@NoNameAtAll2
2 жыл бұрын
logN times slower for... what?
As an old Curmudgeon who started with Fortran 4 many years ago, as interesting as this review is, I see three deficiencies. (1) Seems that pretty programming is more important than execution. As a Curmudgeon, I solve math problems. (2) There was no discussion of the translation of the code into assembly (needed to optimize code of reasonable complexity, excluding (?) GPU processing) - but I guess since 'pretty' is more important than execution, why bother, (3) And finally, I guess that knowing math is no longer necessary for programming when all you need to do is call a pre-defined system function.
I wanted ocaml n zig :(
@charlesselrachski34
2 жыл бұрын
what about roc lang running in zig ?
What font are you using in the code examples
Thanks Very Nice Congratulations from Brasil Best Regards :)
so many interesting comments!
Yes, you can pass arrays without declaring size or dimension in fortran. Best language there is for scientific computation by far; also, implicit none is not actually required but recommended. Also, who declares the variables like that nowadays, I got C ptsd from looking at that.
APL syntax reminds me of my old Perl code (in a bad way). I guess code maintenance is out of the question and you should always start from scratch. Or maybe it's just my ignorance talking.
I was disappointed, because it boiled down to assembling invocations of pre-existing functions. I would have preferred something like the “99 bottles of beer” programming demos, where one implements min(), max(), and gcd() from scratch, using only whatever constructs one has in one’s programming languages. Like this for min and max of nums in Python: nmin = nums[0] nmax = nums[0] for n in nums: if n if n > nmax: nmax = n
The Julia function for finding the GCD is way too long: `find_gcd(a) = gcd(extrema(a)...)` is a lot shorter, and more efficient
Surprised C++ was so low when it's single-pass!
@VivekYadav-ds8oz
2 жыл бұрын
I think that's mainly because that could be done in any language if performance is what's important, which in his ranking clearly was not. But still, for someone who focused so much on stdlib completeness (gcd should be in stdlib), it feels weird he didn't appreciate this enough.
missing import for Python import math my version of Python requires an import for List but you can replace List[int] with list to avoid another import.
Go Forth, young man.