Everytime I sit down to learn std ranges I end up giving up because whatever I'm doing is not supported by my compiler yet or there is something like this which makes me want to take a nap and wake up when C++ is better.
@UsatiyNyan9 ай бұрын
Just use open source ranges library, it’s basically what’s coming to c++, but already complete.
@sleblanc9 ай бұрын
This is what C++ does to your brain.
@vintagewander9 ай бұрын
honestly I don't even know what C++ is trying to be at this point, I lost connection with C++ since C++17, and now I'm completely lost
@michelians11489 ай бұрын
In visual studio intellisense will give you lots of errors but it will still compile. The solution is to disable intellisense.
@KPidS8 ай бұрын
Or just use Rust which has excellent tooling and amazing functional programming features built into the language from day one 😎
@UsatiyNyan9 ай бұрын
It’s probably pretty obvious, but, having 100 bools in the array that signify whether index is prime or not, will be faster. And also you can precalculate them in compile time since c++17, but I understand, that that wasn’t the main goal of the video.
@mitigamespro87579 ай бұрын
Nice finally someone said it.
@KingKarEl1009 ай бұрын
Optimize it even more for space by using a bitset instead of an array of bools
@surters8 ай бұрын
@danielhalachev4714 You can do a lot of constexpr computing primes less than 100 OR type them in from a known list of primes.
@Alan9101278 ай бұрын
@danielhalachev4714 In this problem, you can just hardcode it like the `constexpr std::array primes = {2, 3, 5, 7 // ...` in the video
@UsatiyNyan8 ай бұрын
@danielhalachev4714 you make a constexpr function is_prime, and in another constexpr function you go through 100 indices and fill the array, for example.
@orbital13378 ай бұрын
One of the guiding principles for the C++ ranges library was the idea of always returning potentially useful information that is computed during the algorithm. The original proposal for `find_last` and its variants did in fact only return the iterator. However, this throws away information in the case of a forward range with a sentinel. In that case, `find_last` must find the actual end of the range and not returning it would waste this information that took O(n) time to compute. The asymmetry is because ranges themselves are inherently not symmetric since they have a begin iterator and an end sentinel.
@japedr9 ай бұрын
Couple of things: 1. As a performance paranoid guy, I would use uint8 as the type of the array so that could fit more easily in cache. And then hopefully the compiler would prefer SIMD registers, but I did not check that. 2. There's std::ranges::contains, since C++23. I always disliked the "compare to end" idiom anyways. Great work as always.
@code_report9 ай бұрын
Wow, right you are on #2. I actually searched for it but not hard enough because I landed on the contains method on string, set and unordered_set. This is awesome! I have updated the compiler explorer link to reflect it. en.cppreference.com/w/cpp/algorithm/ranges/contains
@kamilkarwacki95909 ай бұрын
Another thing about performance, shouldn't binary search be used to search if the number is in primes array? They are sorted. Am I missing something?
@code_report9 ай бұрын
@@kamilkarwacki9590 As i mentioned in response to another comment: For 25 elements, std::array will almost always be faster. I profiled both std::ranges::binary_search on std::array and std::unordered_set and std::find on std::array was by far the fastest.
@Megalcristo29 ай бұрын
@@code_report What about std::ranges::find_first_of(), there is no std::ranges::find_last_of(), so you would have to do .base()-1, but you can delete is_prime() that way
@surters8 ай бұрын
@@code_report Should be faster to use a int64_t primes representing all uneven numbers below 100 and setting the bit corresponding. This could cause it to be a immediate in machine instructions so no memory access at all. return (num & 1) & ((primes & (1 > 1))?1:0); // might be optimized more.
@gg1k9 ай бұрын
I did this in rust when I saw the video & nums.iter().enumerate().find() makes it pretty simple. Then adding .rev() for the last prime is nice because it's exactly the same code.
@hesampakdaman82048 ай бұрын
That’s what I also did until I discovered you can use position() instead of enumerate().find(). And with rposition() you don’t even need rev()! That’s awesome Rust 🙌
@Fudmottin9 ай бұрын
Still trying to catch up with C++98!
@andrez768 ай бұрын
C++ is already such a huge and complex beast. The last thing it needs is more asymmetry. I share you sadness and wish that the committee would be more careful about letting those things pass.
@ElementaryWatson-1234 ай бұрын
why don't you apply joining the committee if you are so much smarter than they are so you may straighten them up😂
@leshommesdupilly8 ай бұрын
- Noob: Just use for loops :D - Average: Nooooo, you need to use std algorithms and templated functions to make the code more in-line with the modern standards !!! - Senior dev: Just use for loops
@mrdubachery8 ай бұрын
I'm glad someone else said this, lol. I'm sitting here thinking, why isn't this just 2 linear searches, one forward and one in reverse, break out of each when you locate a prime.
@muggzzzzz8 ай бұрын
@@mrdubachery you don't even need to use two cycles one after another. Just do a forward loop and compute the reversed index inside the same loop. And stop looking when you find both the first prime and the last prime using these forward and reversed indices.
@mrdubachery8 ай бұрын
@@muggzzzzz2 loops is optimizing for the worst-case
@ElementaryWatson-1234 ай бұрын
there shouldn't be linear search used in the first place, the binary_search should be used for that sorted range or lower_bound/upper_bound for that particular task
@raykirushiroyshi27529 ай бұрын
Bruh, im a new to c++ and ive been coding in it how you would code in C. There are sooo many language features that would take me more time to find than to implement them myself. Like do you really need .begin and .end if you know the ranges? The std::distance was even more baffling to me. So much abstraction for so simple stuff,imho.
@user-sl6gn1ss8p8 ай бұрын
yeah, this seems like crazy overkill. Maybe it's a case of if you're used to it in contexts were it makes sense, than you just use it wherever?
@AssemblyWizard8 ай бұрын
Sure but how much time would it take your coworkers to read it? It's much easier to reason about library functions than a for loop which could be doing anything
@user-sl6gn1ss8p8 ай бұрын
@@AssemblyWizard I'm pretty sure I'd get it in a glance, and I really don't think I'm above average, at all. These things have their idioms as well, and if you're in a codebase were they're the style, you'll quickly recognize them. But like, I'm talking about stuff like this example, which is very simple - I get that for more complex stuff this can change.
@eldonad8 ай бұрын
Most of the complexity of C++ is motivated originally by providing safety to the language : very hard to buffer overflow with an iterator based for loop or a string versus a null terminated C style string ; RAII, all the flavours of smart_ptr, reference types help to keep the memory safe by clarifying how ownership of the data is organized in your codebase ; template shenanigans and constexpr are supposed to provide a slightly less obtuse and type safe alternative to macro hell... The thing is, for a solo dev this kind of "strict safety" might not seem very useful, as it's not that hard to write a range based for loop without messing up, but writing ten thousand correct for loops in a 500k loc project is a lot less easy. Also, the relative complexity when compared to other programming languages like C# or Python comes from the fact that C++ is performance oriented, like C, and thus its abstractions strive to be "zero cost", which means all of the work is done by the compiler and you can't "cheat" by, for example, checking your pointer values every time before accessing them. Of course all that being said, the C++ community is not devoid of people that have a strange adoration for abstraction layers and template wizardry. It's _fun_ , but rarely used in actual production code. Companies usually have a policy where they allow a specific subset of C++ to be used so that you get the benefit of safety with a limited amount of added complexity.
@jamesarthurkimbell9 ай бұрын
In my mind I'm always expecting a "negative index" type of result, i.e. the distance from the right, whenever I'm doing an rfind or similar. And that's never how it works.
@connectety9 ай бұрын
If you do a UIUA solution: - there the is memo modifier for memorization. It improves my worst case performance from around 10.5s to 10. - you can prime factor numbers using un reduce multiply - you can measure the time a function takes with under now FUNC
@Whatthetrash8 ай бұрын
This is fascinating. I can't tell if this channel is trolling or not. The solutions presented -- though they work -- are completely unreadable. The standard libraries and functions obfuscate all they do. There's no way to look at this and trivially follow the logic of what this does (which -- I was taught -- you should be able to do). Part of what I try to do as a programmer is answer things as simply as possible (which isn't always as tersely as possible). If it takes me considerable effort/ exhaustive documentation to read and follow my code, I know I've messed up somewhere. I'm not saying yours is bad, I'm just saying different strokes for different folks. As a demonstration of the various functions available in the various versions of C++ it is successful. Props -- and thanks for sharing it. :)
@RafaelNoronhadeOliveiraSoveral8 ай бұрын
WTF this is completely readable and i'm speaking as someone with little knowledge on C++ From a FP point of view, all the solutions presented here are very wonderful You just sound like someone with code interpretation skill issues or a procedural boomer stuck In the past lmao
@literallynull7 ай бұрын
@@RafaelNoronhadeOliveiraSoveralbased
@filmamundo91944 ай бұрын
this is pretty readable, the only overkill is the std::distance in my opnion. If you want to make it prettier just use namespace std; or apply an alias to ranges
@jongeduard8 ай бұрын
Apart from still horribly unsafe, C++ still interests me at moments, because it's also an fascinating language as a showcase of a lot of possible things that a language can have. Even though do not touch it every day. But such strange inconsistencies as shown in the video are indeed a bit surprising. A historical reason for that would also surprise me, since both version 20 and 23 are really recent years. Does anyone know more about this? Apart from that, the solution to this challenge in both C# and in Rust the solution for this challenge is still far simpler. Rust is a great language beyond any level, but interesting about C# is that it has a very simple First and Last method to which a lambda expression can be passed as a predicate.
@misana778 ай бұрын
You don't need to call cbegin, cend if the object already constant. The regular begin and end will return constant iterators just fine
@gtgunar9 ай бұрын
You could precalculate 2 arrays with the "next prime" and the "previous prime". The next prime would store prime numbers at themselves as the index. Then the same prime numbers "brushed" towards the smaller indices on the next prime array, all the way until reaching an other prime. The same thing towards bigger indices, for the previous prime array. When you look for differences, you just look into the arrays, and subtract. Constant runtime with linear memory requirement for handled possible valuerange. BTW, can you look into BrainF interpreters in different languages? I made one for APL, and I'm curious if it can be improved in BQN/UIUA( very likely). THX!
@neamupanselutelor73099 ай бұрын
What's the point of writing 'auto is_prime() -> bool'? why not 'bool is_prime()'?
@surters8 ай бұрын
He is used to program in another language where that is the norm.
@F100cTomas8 ай бұрын
C++ is like 3 programming languages fused into one
@kebien60208 ай бұрын
Function names line up better if you use trailing return type for everything. (Though, for void and bool they line up the same either way. So probably just for consistency)
@maezelbop8 ай бұрын
It's mostly personal preference, but the trailing return type syntax has the advantage of visually separating attributed and specifiers like `constexpr` from the return type and its own modifiers like const. It's also useful in templates where the return type depends on the names of the function arguments, and so must be specified after those arguments.
@paulybarros9 ай бұрын
Very cool video idea
@mehdiyahiacherif23268 ай бұрын
I like how dynamic languages are trying to be static like python hints and typescript Static panguages are trying to be dynamic with var, and removing types from definitions
@uis2468 ай бұрын
What's with syntax? Since when return types in C++ can be expressed as -> type?
@konsth1918 ай бұрын
since c++11, if you place "auto" in the traditional return type location, then you can write the trailing return type with ->
@Tyranisaur9 ай бұрын
When you search for the last prime, shouldn't you use the iterator to the first prime, to limit the scope of the search?
@__christopher__8 ай бұрын
Since the second search is reverse, it will stop at latest at the first prime anyway.
@AndrewHelgeCox7 ай бұрын
It makes sense to terminate the contains algorithm early if a prime greater than the number being looked up is seen. Like this: template constexpr auto contains_early_terminate(R&& range, const T& value) -> bool { for (const auto& elem : range) { if (elem > value) { return false; // Terminate early if an element greater than the value is found } if (elem == value) { return true; // Return true if the value is found } } return false; // Return false if the value is not found } constexpr auto is_prime(int n) -> bool { // return std::ranges::contains(primes, n); return contains_early_terminate(primes, n); }
@DAV303718 ай бұрын
such a awesome language that gives programmers so much options!
@NithinJune8 ай бұрын
i wish you just explained what find if and find and find last if did
@danignat2928 ай бұрын
Using a std::set would get constant complexity on finding elements, much eficient and cleaner to write
@filmamundo91944 ай бұрын
he already commented binary search and hashset were slower than linear search for this small amount of elements
@diamonddemon76129 ай бұрын
And this is why I use C. Dont know what version im using but it will be used forever and only break when i tell it to
@user-sl6gn1ss8p8 ай бұрын
to be fair, no one is forcing the guy to use std::find and such
@kartikpintu8 ай бұрын
I mean, just because we have a C++23 doesn't mean we can't still use C++11. Use what you can and only worry about squeezing performance with newer versions only if it makes you richer or when your manager nags at you. The best code is the one that everyone can understand and debug.
@thebatchicle34298 ай бұрын
@@kartikpintuThe issue is when others put C++23 into your project
@skeleton_craftGaming8 ай бұрын
Unless you are working on an existing project, not only is there no reason to use older versions of C++, I would argue that it's objectively wrong to. [This video is a perfect example of why]
@alexiscomix8 ай бұрын
title unclear, i read 5 problems
@CielMC8 ай бұрын
6:30 I would call it drop until, but yeah
@SvetlinTotev8 ай бұрын
No sane person would ever use a significant fraction of these features. Language features and libraries are useful when they help you get your work done faster, easier, safer, or in a more performant way. This code uses features for the sake of using them rather than for the sake of getting the job done in an acceptable way.
@EnDeRBeaT8 ай бұрын
Will it do the work safer? Of course it would, index based for loop has like 4 ways you can make a mistake. More performant? Probably yes, if you don't use ranges, you can add std::execution::par to the start of the function to make it parallel, ain't that fast? Faster? Depends, if you are familiar with algorithm header, and you leverage the power of autocomplete, then you will probably be faster. Easier? Probably not, maybe if you consider in the blunders that you occasionally can make in your code. I honestly use a lot of algorithm capabilities, and I don't think they are "features for the sake of features" I like range based for more however
@SvetlinTotev8 ай бұрын
@@EnDeRBeaT Straw man. You are arguing with yourself. - performance is irrelevant here because the compiler will optimise almost any implementation to the same simple loop. But yes, without optimisations the ranges are worse because you loop over the data multiple times. Something that compiler optimisations handle very well. - "if you are familiar with xyz" - yes, if you are familiar with every function in the std you might be just as fast as writing the single expression that does the exact same thing as the std function. If we ignore the time it takes to memorise the std. The question here is why would you write twice as much code to abstract away all the trivial logic to functions that everyone reading the code would need to look up to see what they do. - Not even beginner programmers would mess this up. getting indices wrong is common when you have nested loops or multiple arrays. But here you have one array, one loop, and one index. Though I would still use "enumerate" here just for the extra safety and readability. My point here isn't that ranges are objectively bad. My point is you should write the simplest and clearest code that does what you want. Abstracting code away only helps for complex logic where interpreting the intended behavior from the function name is clearer than writing the logic itself. That's why in this example I suggested using enumerate to clearly state that you intend to loop over all the elements and their indices, but the logic for finding the first and last based on condition is clearer if it is written out.
@secondson45369 ай бұрын
Why? You could write this C-style longer by about 2-3 lines. Instead this, which is abhorrent in its every version - long and ugly names, nightmare to a person who doesn't want to deal with all of std::ranges and std::views bullshit and plain less understandable. What is wrong with old style loops? Did everyone agree overnight that C++ is now a functional language?
@QUIKScopersclan9 ай бұрын
I would agree with at least item based for loops. Old C style loops can be dangerous cause of out of bounds violations. You can say it’s a skill issue all you want, but removing the footguns to begin with is a good practice nowadays
@testtest-qm7cj9 ай бұрын
To be fair, code_report is not saying you should do it his way; your C-style approach is still valid and I believe a lot of us would do it that way. It just happens to be that code_report really likes functional style programming, hence showing solutions in FP whenever he can. I find his C++ conference presentations informative to watch. With that out of the way, I would say whether we want FP in the first place in any language is still an open question. I like the FP's idea to program by declaring what you want, and not by typing how you do it. I, however, agree with your feeling that it makes you wonder if FP is the right amount of abstraction. Sometimes it hides too much, and makes you feel like you're using a script language. On the other hand, maybe that is the point. Some problems are so mundane that you ARE essentially scripting. All I want is C++'s implementation on these range algorithms to be as performant as it can be. Then, it's just another useful tool we can use.
@PhthaloJohnson9 ай бұрын
Doing it with iterators is better, but yeah it's conflicting because the C style is just so much simpler. This is why Rust feels quite a lot nicer to use iterators with, they were designed to work with the language.
@secondson45369 ай бұрын
@@QUIKScopersclan I guess that's why I don't like Rust. People talking about footguns when writing a loop. I don't wanna sound offensive, but if a loop is too dangerous for you, might consider switching jobs... I understand the point, however if that means that codebases I browse will be looking like this, man... I'd rather code in java
@secondson45369 ай бұрын
@@testtest-qm7cj I guess you're right, but I expected different ways to view the problem when the title is "Solving one problem in 4 different X", not just refactoring functions into newer functions that do the same. There was a video called "C to C++ to Rust to Haskell", and while I think the C++ code looks ugly there too, the author at least acknowledged that it is simply an alternative way to do the same thing.
@budgetarms8 ай бұрын
I mean C++11 is outdated, it's now C++14, I mean, if you look at vs
@davidfrischknecht82619 ай бұрын
Isn't C++23 still a draft standard?
@Spielix8 ай бұрын
"The standard was technically finalized by WG21 at the hybrid meeting in Issaquah in February 2023." From Wikipedia article "C++23"
@davidfrischknecht82618 ай бұрын
@@Spielix I consider it a draft until the non-draft version is officially published.
@oschonrock9 ай бұрын
can't is_prime() just index into a pre-populated (and optionally computed at compile time) array of 100 bools and return instantly? O(1) rather than O(N) and many times faster?
@ghevisartor60058 ай бұрын
Damn gotta remember this
@AndrewHelgeCox7 ай бұрын
Yes and those 100 bools can be bits of an 128bit-wide SIMD register if you are willing to dip into `std::experimental`. BTW O(1) == O(2) == O(3) ... == O(25) so the find_if()s are already O(1): we are nitpicking the costs of the constants, not changing the orders of the algorithms.
@RawFish2DChannel8 ай бұрын
I program in Java and a little bit in C, but I sometimes think "maybe I should use C++ instead of C?". This video reminds me that C++ has so many good features, but also it has too much different features and stuff like this, and it's pain for me to understand what they all do and mean.
@sweetcornwhiskey8 ай бұрын
As someone who started coding in c++ about a year ago - you really don't need these features for the vast majority of applications unless they're required by your employer. These features are interesting and neat, but they are absolutely not necessary to know to be able to code in c++, and I often find that it's much easier for me to use "bad" c++ code that uses very little of these features. And if you don't use these features, then you don't have to know what they mean
9 ай бұрын
Does constexpr make is_prime O(1)?
@antagonista81229 ай бұрын
It effectively gets desugared into classic for loop, but constant propagation can unroll it into if statements cascade which then can be turned into e.g. jump table if compiler decide to do so, but it has nothing to do with constexpr itself, but with optimizations. + this code is already O(1), as the commenter below has already pointed out.
@fullfungo8 ай бұрын
It’s already O(1) even without optimisations and even without constexpr. You should refresh your memory on what O(1) means. Since in the worst case it will perform 25 comparisons, the execution time is bounded by a constant value. Therefore, it is O(const) which is the same as O(1).
@rursus83549 ай бұрын
I think I'm going to abandon C++ forever, and I'll prefer D.
@vimdiesel9 ай бұрын
Deez nuts
@Nylspider8 ай бұрын
Don’t think anyone has pointed this out yet so I’ll be the first to say it Is the title an I Wanna Be The Guy reference? Because I love it lol
@justinliu77889 ай бұрын
bruh inconsistency
@c4tubo8 ай бұрын
All those yellow namespace specifiers annoy me now more than ever, and that's your fault because my new addiction to array languages have made the wordiness of everything else so glaring--even in Lisps it feels like too much now.
@__christopher__8 ай бұрын
Listing all the primes seems not very effective. It's a lot of typing (and a lot of opportunity to mistype) and not easy to check at a glance that you got it right. Here's my solution to is_prime in C++20: // for a number between 1 and 100 (inclusive), check if it is prime bool is_prime(int const num) { // special case: 1 is not prime if (num == 1) return false; // primes whose square doesn't exceed 100 std::array small_primes = { 2, 3, 5, 7 }; // find a small prime that divides num auto result = std::ranges::find_if(small_primes, [=](int const prime) { return num % prime == 0; }); // if we didn't find one, num is prime if (result == small_primes.end()) return true; // otherwise it is prime iff it equals the prime that divides it return num == *result; }
@cariyaputta8 ай бұрын
Ah, the asymmetries..
@motyakskellington77239 ай бұрын
i would just use c++17 version, or c++11
@_jaime.9 ай бұрын
Would it be better to use an unordered set for the primes?
@code_report9 ай бұрын
For 25 elements, std::array will almost always be faster. I profiled both std::ranges::binary_search on std::array and std::unordered_set and std::find on std::array was by far the fastest.
@314Labs8 ай бұрын
I agree with you on how find_if and find_last_if should have the same return type. A bit disappointing. Great video!
@elkvis9 ай бұрын
I always find it hilarious that evangelists (sleazy salesmen) for other languages (Rust, I'm looking at you!) often criticize C++ for having 4 different ways to do everything. Meanwhile, there's a dozen crates in Cargo for every possible thing you can imagine, all of them similar enough to be all but indistinguishable from each other.
@PhthaloJohnson9 ай бұрын
I think the keyword there is crates. First, Rust crates are not part of the standard language, are easy to install and incorporate. This makes them opt-in, per project dependencies. In C++, all of this non-sense in built in to std. At this point, I think C is a better alternative to C++, honestly. I'll miss the higher level algorithms and functional features, as well as some of the nicer data structures and nothing else.
@DTD3698 ай бұрын
I've always been baffled by the absurdity of C++ features throughout different standards, so I either stick with C++11 or just work with C. Seriously, C++ is a joke.
@teamavatar64609 ай бұрын
cpp has become a joke
@Tomab38 ай бұрын
Not a joke and its widely used and will be used in the future, it has just become overly complicated and verbose
@ic3man58 ай бұрын
@@Tomab3 one could argue overly complicated and verbose is a joke.
@cmilkau8 ай бұрын
If you care about performance enough to store the primes, you should not use such an extremely poor implementation of is_prime. I mean at this point you might even be faster with a standard remainder test.
@AzureFlash9 ай бұрын
C -> C++ -> C++++ -> C# -> C## -> C▦
@markusfischhaber81788 ай бұрын
Modern c++ is just a mess of library and a mess of language
@mieszkomazurek30338 ай бұрын
Good example why C++ is a terrible language and it goes to nowhere