From C ➡️ C++ ➡️ Rust

Поделиться
HTML-код
  • Опубликовано: 4 дек 2024

Комментарии •

  • @jonhdoe1395
    @jonhdoe1395 Год назад +2260

    Congratulations, you took perfectly readable code, with a logical progression, easily debuggable and turned into a nice spaghetti. The boomer in me would say "that's what is wrong with the new generation".

    • @peterheijstek5288
      @peterheijstek5288 Год назад +405

      It's declarative instead of imperative. If you know what the functions do, then the declarative code is not only more readable but also less prone to bugs because you are using functions in the standard library.
      The entire craft of programming is composing different pieces of code, right? This is exactly what declarative code does best
      Edit: as others have pointed out, in the C++ case the original comment is justifiable. I thought the parent commenter was talking about languages like rust and haskell

    • @DylanFalconer
      @DylanFalconer Год назад +450

      I know this is meant to be the pin of shame but I agree in the case of C++. It's so difficult to understand compared to the original and Rust versions.

    • @DylanFalconer
      @DylanFalconer Год назад +25

      Cool video though

    • @sohn7767
      @sohn7767 Год назад +97

      C++ Version really was that

    • @esra_erimez
      @esra_erimez Год назад +75

      I was thinking the exact same thing myself. I'm glad I wasn't the only one

  • @ThePrimeagen
    @ThePrimeagen Год назад +526

    see the thing is that iota's greater instruction count is due to the fact that iota, being the smallest character, sometimes have the largest impact, both metaphorically and literally.

    • @El-Burrito
      @El-Burrito Год назад +3

      I just subbed to your channel, funny seeing you here

    • @alexandriap.3285
      @alexandriap.3285 Год назад +19

      tf does that mean

    • @praus
      @praus Год назад +21

      @@alexandriap.3285 Yes

    • @mylkrenebjorson7244
      @mylkrenebjorson7244 Год назад +4

      @@alexandriap.3285 The numbers of the instruction generated doesn't necessary mean anything.

    • @Brono25
      @Brono25 Год назад +21

      Really makes you think

  • @wikinut1
    @wikinut1 Год назад +1681

    To me, the C code is perfectly clear and readable, and the Rust and Haskell is perfectly clear and readable. However, whenever I look at C++ code like this, it looks like an accident waiting to happen with it's amazingly aggressive overloading of syntax.

    • @Brock-Landers
      @Brock-Landers Год назад +65

      The iterator being declared in the for loop triggered me.

    • @austinmajeski9427
      @austinmajeski9427 Год назад +154

      I was thinking the same thing.
      It got less readable by the end of the C++ code. “One-Liners” are not always the best option when it comes to clarity.

    • @dixztube
      @dixztube Год назад +5

      @@austinmajeski9427 yup lol

    • @redcrafterlppa303
      @redcrafterlppa303 Год назад +29

      I think of course the clear misuse of operator overloading by the standard (which is ironic by itself) is a big problem in c++ but another great problem is the baggage c++ is carrying. They constantly add new features and libraries to std which build on the old libraries and therefore they never retire old libraries. This leads to a thousand ways to write the exact same assembly instructions. Also trying to forcefully reuse older parts of the library prevents them to construct fresh and simple new APIs like in the ruet example.
      Hack the same code can be written in c++ if you write the required library code from scratch. So it's not necessarily a problem of the language but the rotten standard library.
      One argument against this line of argumentation is that they keep backwards compatibility while reusing code they can't therefore remove anyway. But as I explained this leads to bad APIs and isn't the best solution to fix incompatible libraries. A better is again the rust way. In rust every library and program declares the std library and 3rd party library versions its using. The cargo build tool is then compiling each library with its required versions and links them together. Therefore older libraries can use removed std features while being used in never applications and vice versa. This even applies to the language version and keywords. Therefore impossible combinations of libraries do not exist on rust and the rust committee is free to retire library functions and keywords replaced by better alternatives.

    • @Haranoi17channel
      @Haranoi17channel Год назад +6

      @@bram3367 I wouldn't say its overengineered. Its perfectly readable. The before... Completely different story.

  • @foxoninetails_
    @foxoninetails_ Год назад +468

    A big part of the point in the original video was less about this kind of reduction, and more about style - while this kind of reduction is an excellent technique to go through when you can, the actual code itself was really just an example to point out the underlying issue of code style. The process of pulling out nesting into preconditions is broadly applicable to many different situations, where this kind of code transformation is much more specific and situational.

    • @biskitpagla
      @biskitpagla Год назад +15

      Style is one axis, paradigm another. I think a better sample for this video would've been some callback heavy snippet that's unreadable but still somehow manages to pass as pure and functional. Considering the OG video was about style from within the imperative way of thinking, this would've been fairer. That said, it's really hard to come across code that is pure and badly styled at the same time. I guess this is due to the fact that most programmers start out as imperative ones and branch out to the functional world as they grow. But I do think in languages like Rust it's very natural to write `good` code similar to what is shown here because of zero/low-cost batteries-included standard libraries.

    • @arisweedler4703
      @arisweedler4703 Год назад

      The style that even makes it possible to nest like this is impossible in the paradigm of shape thinking, of functional programming.

    • @foxoninetails_
      @foxoninetails_ Год назад +39

      @@arisweedler4703 I strongly disagree. While a functional style naturally tends towards nesting less, it is still certainly possible to write deeply nested code in functional languages, and there are - albeit rarely - situations where it's needed.
      But the point remains: the original video's intention was to point out how to deal with nesting better in a more imperative paradigm, by making use of preconditions and early exit. Responding to that with "just change to a functional style" may indeed be the best practice path in many cases, but it misses the point entirely, and is a much less broadly applicable principle.

    • @arisweedler4703
      @arisweedler4703 Год назад +5

      @@foxoninetails_ else you said, yes. Totally agree. Lisp can get pretty nested. I took a class in college that used lisp and I didn’t know what I was doing and my friends and I wrote some successful and bad code. Nested deep.
      And as for the point of the original video, yeah. It was like “we’re imperative, this is better” and it is, I agree. In fact, the “bug” where there are two ranges used in the control flow and yet they are off by one is no longer a bug in this new style. It is now contextualized as an intentional bounds check using one range, then a map filter / APP one-liner / shape pipeline over another, similar but different range.

    • @chrism6880
      @chrism6880 Год назад +16

      I love videos about refactoring, because it's like watching a fantasy movie about a world where we have time to reduce the tech debt in our codebase

  • @VincentZalzal
    @VincentZalzal Год назад +190

    For those who are wondering, the sudden reduction in the number of instructions at -O3 starting from filter comes from the fact that the compiler was using SIMD instructions to handle multiple loop iterations at once in the previous versions, but after that, is unable to see through the views to perform the same optimization (which is disappointing). Edit: see reply below for better news!

    • @VincentZalzal
      @VincentZalzal Год назад +40

      Now I am confused... When trying the same code in QuickBench, the calculate functions get inlined, and then gcc optimizes the version with views using SIMD too, but with a way tighter inner loop... and that makes the views version 3.7 times faster than the original when operating on very large ranges. I might have made a mistake somewhere, I don't fully trust how I used QuickBench. Maybe someone else could try it out? For info, I was benching the call calculate(54, 12393243) with versions 1 vs 4 in the code_report Github repo.
      Edit: after further tests, it seems the views version get optimized with SIMD when inlined and using literal arguments. But when I pass in volatile ints as arguments, then I lose the SIMD optimization in the views version and the original version is now 3.4 times faster. So yeah, it is quite sensitive.

    • @pesto801
      @pesto801 Год назад +3

      @@VincentZalzal I verify the same result. Any ideas where this might be coming from?

    • @VincentZalzal
      @VincentZalzal Год назад +6

      @@pesto801 No idea, I'd have to analyze the generated assembly to understand how the versions differ. We might be right on a threshold the optimizer uses to decide whether it is worth it or not to use SIMD.

    • @R4ngeR4pidz
      @R4ngeR4pidz Год назад +9

      Reduction?
      Am I color blind, am I not reading this correctly, or are you not reading this correctly?
      For -O3, they use the color blue, and the blue bars are by far the highest for the first ones, which makes no sense because -O3 would optimize the crap out of the original code
      so are the colors in the legend supposed to be reversed?
      Or, also a likely explanation: the number of instructions means nothing, without the context of what those instructions are

    • @VincentZalzal
      @VincentZalzal Год назад +21

      @@R4ngeR4pidz You are reading this correctly, blue is for -O3, and the number of instructions decreased when using views. The reason is that the code size usually increases when the optimizer starts using SIMD (versions on the left of the graph). It increases because the compiler has to add code before and/or after the main loop to handle the last few elements if it cannot guarantee that the number of elements is a multiple of the SIMD register size.
      So, to recap: versions on the left uses SIMD and thus have more instructions, those on the right don't use SIMD and are shorter. And yeah, as you say, the number of instructions is not necessarily a good metric for code performance.

  • @eduardoalmeida4734
    @eduardoalmeida4734 Год назад +193

    Keep in mind that pure # of instructions may not be the only "optimization" parameter to be observed. C++ often optimizes code to run faster (having less branches) and this may lead to a few more instructions but with HUGE gains in execution time.

    • @vercolit
      @vercolit Год назад +33

      -O3 is loosely -O2 but also with aggressive optimisations that increase the code size. So usually it has more instructions, but they end up being faster. Sometimes, though, it isn't faster, because the increased size of the code increases the amount of cache misses to a poibt where it decreases performance. So imo the number of assembly instructions is simply not a great measure of performance. Very interesting to see and definitely an aspect of performance, but clearly not the only one

    • @skeetskeet9403
      @skeetskeet9403 Год назад +1

      This is true of C, C++ and Rust. They all have optimizing compilers for a reason

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      @@skeetskeet9403 All compilers have grown from C, have the same heritage and basic optimization techniques. The first C++ compiler was written in C front end that generated C code. In time it's flipped, and now C and C++ compilers are written in C++. Most other compiler also started as C++ and only in time some were able to go native.

    • @skeetskeet9403
      @skeetskeet9403 3 месяца назад

      @@ElementaryWatson-123 I don't see your point. The heritage of a compiler is entirely irrelevant to its performance and optimizations it's able to apply.

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      @@skeetskeet9403 of course, it's relevant

  • @cd-bitcrshr
    @cd-bitcrshr Год назад +55

    Love watching c++ devs argue which of their unreadable buggy code is more unreadable than the other

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      Readability is the ability to easily understand the code, it not only depends on the code itself but also on the reader's expertise. If you don't know Japanese, any of the books written in that language no matter how beautiful, would look to you like gobbledygook.

  • @japedr
    @japedr Год назад +101

    I know that's not the point of the video, but this can be solved with a very simple O(1) implementation by using the arithmetic series sum formula. That would have very few assembly instructions also :)

    • @dwarftoad
      @dwarftoad Год назад

      Yes would be interested to see examples for some arbitrary input sequence. Which makes the awkwardness of needing to use C++ range's std::ranges::iota() less of a problem, but you still might need it to generate some indices to something else, so I still like Rust's terse (begin..=end) and wish C++ had something like that (especially to be used in for loops).

    • @Brmngm
      @Brmngm 6 месяцев назад

      EXACTLY!!! #$%^&*

    • @anonanon6596
      @anonanon6596 3 месяца назад

      I know this is not what this comment is about, but technically using the arithmetics series sum formula would be O(n log n).
      That is because a formula is only as fast as its the slowest part, and multiplication is O(n log n) (This was proven quite recently actually, like in 2019, but it is very complicated, there is something simpler you can read about though, it is called Karatsubas algorithm, a guy called nemean has a good video about it)
      In practice you are limited by the variable size so you would have to implement a custom data structure to handle that, and only then the time complexity would take effect.

    • @japedr
      @japedr 3 месяца назад +1

      @@anonanon6596
      Well, that is true for arbitrary precision numbers (i.e. bignums). But usually we restrict ourselves to employing fixed sized numbers (e.g. 64 bit) that can be fed directly into the CPU's hardware multipliers, and those require constant time (a known number of cycles).
      Adding two numbers has the same issue, we need to traverse all bits (i.e. log2(n)) and the carry bits need to be propagated.

    • @anonanon6596
      @anonanon6596 3 месяца назад

      @@japedr I said "technically". I was being pedantic.
      But if we limit ourself to a single architecture, doesn't using big O lose some of it's meaning?
      Like, there are instances where N^2 is faster than N for small input, but small is a meaningless word in mathematics, it can be arbitrarily large.

  • @wChris_
    @wChris_ Год назад +34

    When comparing assembly instructions you need to be very careful, since many instructions can execute faster than few instructions, especially when you unroll loops and use simd. Thats why its *always* better to benchmark.

  • @DetectivePoofPoof
    @DetectivePoofPoof Год назад +35

    C++ segment: "Already here you can see by using more modern features we have created an absolute abomination the very sight of which has caused God to abandon us!"

    • @MaxAbramson3
      @MaxAbramson3 9 месяцев назад +1

      Think of how much more readable the same code would be in Whiteboard C.
      #include "StandardIO.h"
      int calculate(int bottom, int top)
      if (top > bottom): int sum = 0
      foreach bottom in top: (number % 2 == 0): sum += number
      return sum
      else: return 0;
      main():
      println(calculate(5, 12)) // 36
      println(calculate(5, 3)) // 0
      So many C++ devs have spent so many years putting in feature requests that leave them feeling like they've contributed something to the whole, but we now have high level languages that take LONGER to understand and create MORE opportunities for readability and maintainability problems over the long run.

  • @SouravDatta
    @SouravDatta Год назад +60

    I love how elegantly we can communicate the solution in functional languages! Here're scala and clojure and they are basically the same as Rust - I like them all!
    def calculate(bottom: Int, top: Int): Int =
    (bottom to top).filter(x => x % 2 == 0).sum
    (defn calculate [bottom top]
    (->> (range bottom (inc top))
    (filter even?)
    (reduce +)))

    • @TheVirtualLab
      @TheVirtualLab Год назад +7

      Every time I look at scala code I think to myself that the code looks super nice. I'll have to find some time in the near future to dive more into it

    • @keychera
      @keychera Год назад +3

      +1 for clojure!

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      All those languages lack customization necessary to solve real life problems. People always show trivial toy examples to show off how some language looks simple and concise. Then you ask them to write something even modestly more complex and you see how all the ugliness starts coming out. The same RUST, you can't write anything practical without "unsafe", and then you write an ugly code resembling C, at which point every C++ developer starts laughing at that.

  • @sagarramchandani3139
    @sagarramchandani3139 11 месяцев назад +1

    Just wanna comment that I am pleasantly surprised by how expressive rust looks like.
    I mainly work with Julia which is also very neat because the equivalent code there would be.
    calculate(bottom,top)=sum(filter(iseven,bottom:top))
    Julia's type inference handles all typing in this case and also handles the (6,6) case you mentioned.

  • @secretagent3712
    @secretagent3712 Год назад +35

    For C version of the program: How about not checking if(top > bottom). It should have been >= instead of >, anyway. Now, if top < bottom, sum is already 0, so when we return sum, we'll return 0. That means that we can completely remove the outermost if-else. We can also go one step further. The expression number % 2 == 0 is either true or false, which is in C interpreted as 1 or 0. When it is true, we add number: sum += number, which is the same as sum += number*true. When it is false, we do nothing, which is the same as sum += number*false. Now, we can remove the check if (number % 2 == 0), and instead of sum += number, we can write sum += number*(number % 2 == 0). The entire function will then look like this (I renamed number as i):
    int calculate(int bottom, int top)
    {
    int i, sum = 0;
    for (i = bottom; i

    • @frydac
      @frydac Год назад +6

      C89 in 2022 :) it's been some time, I do think declaring the loop variable i in the for loop is objectively better.
      Also I don't think it is more readable to add zero, and I would expect the assembly to be the same if there was an 'if' statement, though could be wrong of course.

    • @secretagent3712
      @secretagent3712 Год назад

      ​@@frydac I agree that it isn't more readable, but if the goal is to reduce nesting, this is one way to do it. As for the assembly code, I've never used assembly, so I can't comment on it. Although, it would be interesting to compare performance between different languages, as well as different code (like the one in my original comment).

    • @lesto12321
      @lesto12321 Год назад +3

      Even worse than infinite loop, signed overflow is undefined.. here be dragons

    • @clawsie5543
      @clawsie5543 Год назад +3

      There is even simpler way of doing this. Just round "bottom" to the next multiple of 2 and add every second number. Something like this:
      #define MOD(x, y) (((x) % (y) + (y)) % (y))
      int
      calculate(int bottom, int top)
      {
      int sum = 0;
      for (bottom += MOD(bottom, 2); bottom

    • @secretagent3712
      @secretagent3712 Год назад

      ​@@clawsie5543Yes, this is better. Define is probably unnecessary, though. It should be enough to write bottom += bottom % 2.

  • @PileOPoop1
    @PileOPoop1 Год назад +16

    I am flabbergasted that his complaint about remove nesting was just calling functions and his solution is just to call super convoluted std c++ templated functions that make the code less readable.

  • @Abraham18K
    @Abraham18K Год назад +160

    Question, why not use early return instead of using ternary operations? They are in my opinion simpler to read since you have all the exit conditions at the top and then you just have the code that you want to execute.

    • @rcoder01
      @rcoder01 Год назад +29

      I'd recommend you watch the original video by CodeAesthetic. It goes a lot more into those kinds of refactors.

    • @jaideepshekhar4621
      @jaideepshekhar4621 Год назад +20

      Thank you! Exactly, I HATED that use of ternary operator. Just use a guard clause.

    • @fsalmacis5993
      @fsalmacis5993 Год назад +25

      It's probably because when using a ternary operator you end up with a single expression in the function, which fits nicely with declarative programming. The thing is... even if C++ has become multiparadigm over the years and has functional features, they feel more like an afterthought and are kinda::disgusting::to::the::eye(). Rust, in the other hand, was handcrafted with functional programming in mind, and the readability when coding that way really shows.
      But yeah, the title of the video should be "Declarative Programming from C to C++ to Rust", because an early return would be optimal if we were talking about imperative programming or OOP (which is actually very readable when done in C++).

    • @jaideepshekhar4621
      @jaideepshekhar4621 Год назад +5

      @@fsalmacis5993 Ternary is fine for some cases. For cases like this, atrocious!

    • @minneelyyyy
      @minneelyyyy Год назад

      because its not nearly as fancy or cool looking

  • @Gameplay-ps9ub
    @Gameplay-ps9ub Год назад +73

    The initial code wasn't perfect, not even near, but...I feel like too often people confuse short code with good code. I'm mostly coding in python and it allows to produce a lot of oneliners which do massive work. The issue is though, that quite often they end up being unreadable, which is counter productive in the long run. The first line of zen of python is "Beautiful is better than ugly." and it's the first line for a reason.

    • @shinjonmal8936
      @shinjonmal8936 Год назад +1

      I disagree. When I couple a few related lines in python using ; It increases readability. But however, don't just couple all lines in one line. It decreases readability. This same concept applies to me doing other languages like C#, Javascript etc.

    • @alxjones
      @alxjones Год назад +9

      Less code is not always better, but the examples in this case (at least the Rust and Haskelll ones) are not counterexamples. They are much clearer right out the gate what they do, and even better, it's clear that they actually do what they're supposed to. Python definitely has its issues in regards to having very condensed ways to do things which are harder to read, but Python also has multiple ways to do this that are similar to the Rust/Haskell approaches
      def calculate(bottom: int, top: int):
      return sum(n for n in range(bottom, top + 1) if n % 2 == 0)
      def calculuate(bottom: int, top: int):
      return sum(filter(lambda n: n % 2 == 0, range(bottom, top + 1)))
      The ugly lambda syntax and right-exclusive ranges make it a bit less attractive but ultimately still quite readable. I would prefer to see either of these over the original C code any day.

    • @artxiom
      @artxiom 5 месяцев назад

      Short code can be good or bad and long code can be good or bad. You can write complicated long code but also very readable short code. If you can reduce long code into short code, while maintaining readability it will be always better.
      Not only does less lines mean there can be potentially less bugs but also it's faster and easier to read. You can understand what it is doing "on the spot" and don't have to scroll through a lot of code.
      A lot of code reduction is only about thinking declaratively and using FP functions like map() and reduce() efficiently. Unfortunately a lot of programmers are not taught these. I also learned it only after 10 years of programming...unfortunately, I wish I would have learned it way earlier.

  • @NithinJune
    @NithinJune 9 месяцев назад +1

    i completely agree with you that the final version of the cpp code is more declarative and i like it

  • @winniedobrokot
    @winniedobrokot Год назад +26

    int a2 = (a + 1)/2;
    int b2 = b/2;
    return (a2 + b2) * (b2 - a2 + 1);
    Also can be calculated as arithmetic progression sum without any filters :) Thanks for the great video, I used it as the opportunity to try the Rust

    • @helio6839
      @helio6839 Год назад +4

      This was my first thought as well, although I don’t think the focus of the video is coming up with a more creative solution, but rather a refactoring/reformatting of an existing solution.

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      That's the problem with trivial examples, they are useless when comparing languages.

  • @Speykious
    @Speykious Год назад +172

    I'm sure Rust was more inspired by OCaml than Haskell. After all, the first iterations of the Rust compiler were written in OCaml.

    • @SourCloud
      @SourCloud Год назад

      You here LMAO?

    • @zzzyyyxxx
      @zzzyyyxxx Год назад +17

      Correct, Rust was more inspired by ML languages, however some functional aspects from Haskell still come through, such as how typeclasses and traits are similar.

    • @Speykious
      @Speykious Год назад +3

      @@zzzyyyxxx Yeah, I love that :D

    • @Speykious
      @Speykious Год назад +7

      @@SourCloud I am *everywhere,* as long as it's slightly rusty B)

    • @Hexalyse
      @Hexalyse Год назад

      @@zzzyyyxxx ​ Isn't ML language a repetition ? At this point, I think it's better to write meta language, not a lot more characters, and everybody will know what it's about. Or just write ML but it's also used for Machine Learning so it could be misleading.

  • @fabricehategekimana5350
    @fabricehategekimana5350 Год назад +62

    Super interesting video. I fell in love with rust for two reasons. Firstly because I wanted a language that can do low level code manipulation but a language that nicely implement functional programming (I tried haskell and the experience was lovely)

    • @dmitriidemenev5258
      @dmitriidemenev5258 Год назад +1

      I also liked it for its reasonably good interop with other programming languages and because it promises to become even better!

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      My simple test for a programming language is to write a generic vector. Writing an efficient vector in C++ with custom allocator, placement, avoiding unnecessary initialization, exception safety, appropriate iterators, iterator traits, move semantics, etc. is straightforward. It takes some time but every competent programmer can do it easily. I used to ask that during a C++ interview to see if the candidate knows the basics. Any language that is worth exploring should at least provide a similar degree of expressiveness and flexibility.
      Now welcome to RUST. It turns out performing this simple task is a chore, a code is getting peppered with "unsafe", becomes pretty verbose, and actually looks as ugly as C. C++ is just conceptually richer and more expressive. And another thing you will immediately notice, how poor variadic generics are, RUST traits are nowhere as powerful as C++ concepts. RUST just needs years and years to grow, and at some point it will become a C++ with different syntax.

    • @fabricehategekimana5350
      @fabricehategekimana5350 3 месяца назад

      @@ElementaryWatson-123 Thanks for your return. I have heard that C++ is doing amazing things. I think it will still be needed in the future. But you won't understand the strength of Rust or any other language if you keep your actual perspective
      If you try to use a screw driver like a hammer it will never work since it's a screwdriver and you can do the same with any other tool which is not a hammer. If I try to use any other language like Rust, I could emulate some attribute but it will be most cumbersome than writting directly in Rust. So what's the point of using other languages ?
      The fact that Rust is replacing C++ in some area and even used for the developpement of the linux kernel doesn't mean that Rust is always superior than C++. He is juste better in areas where performance and safety are a must. Of course it's not for free since we lose the legendary expressiveness of C++
      So yes Rust will be an eternal imperfect imitation of C++ long as you consider Rust as C++. But his genesis ans his philosophy are different
      I am happy C++ meets your needs. I am actually designing a programming language and study the differences between actual programming languages to see the features needed for it purpose and Rust is a good candidate to help me design his core calculus

  • @drstkova
    @drstkova Год назад +42

    I understood the original C and it was easy to spot any bugs. As soon as you made C++20 substitutions I thought “what the hell is that?” and the code looked like illogical gibberish. In the commentary you then said “I think this is a lot nicer”! From my perspective you made simple, legible, code totally impenetrable to the point where I’d need to use tools to find bugs in it. I programmed commercial products in C in 1996 and C++ in 1998. I’m trying to figure out whether it’s worth learning this whole new syntax/toolbox that modern C++ requires- in this case it obfuscated the code.

    • @markus4925
      @markus4925 Год назад +6

      I agree.

    • @Zipperheaddttl
      @Zipperheaddttl Год назад +13

      Its not. I program commercial products today (games) and I write code like you did in 1996.

    • @peezieforestem5078
      @peezieforestem5078 Год назад +12

      not only is this not worth it, you might end up getting trapped in the same sunken cost fallacy that so many developers do, where they've wasted a bunch of time learning things that don't really improve all that much, and so not they have to lie to themselves about how the code looks nicer and smarter, and how everyone who didn't sink as much time as them learning useless things is just too stupid to get it.

    • @alvarohigino
      @alvarohigino Год назад +1

      But that's C++ nowadays, a language that provides 1 million ways to do something. That's why we are seeing a lot of new languages trying to conquer its place, bcs nobody wants the new C++.

  • @gigachad8810
    @gigachad8810 Год назад +8

    awesome to see how rust can even rival python in its conciseness and readability from time to time

    • @sebastian6121
      @sebastian6121 10 месяцев назад

      here have some gay ass posing as gigachad

  • @NithinJune
    @NithinJune 9 месяцев назад +2

    5:16 In CPP23 this feature is called “std::ranges::fold_left”

  • @logaandm
    @logaandm Год назад +105

    I don't consider myself a professional programmer, however, in my job as a physicist I have experience with numerical computation and programming with a focus on accuracy and speed. It was obvious that the original code was inefficient and this prompted me to comment and test before commenting.
    I assume the purpose of the example is to show how to modify code to use standard, safer, tested methods and to make the code more readable. Goals I am sure we all share. One of the bigger issues in programing, of course, are errors with unknow size of arrays.
    Not withstanding that there are far better ways to accomplish the same task as the C code, this particular code snip, however, is poorly chosen to illustrate the intended concepts. As pretty as the Rust and Haskell code are I remain unconvinced they are a significant worthwhile improvement. The modifications shown have all the hallmarks of using a sledge hammer to kill a fly.
    Adding even integers is trivial. Even the original C code shows that. Assume "low" is the lowest-even we want to include and "high" is the highest-even we want to include then:
    for(i=low;i

    • @tellur808
      @tellur808 Год назад +3

      Your analytical solution, while very elegant, suffers from one problem: it also doesn't faithfully replicate the original I/O. What about negative inputs or mixed positive and negative inputs? Int is signed by default and negative inputs don't default to zero in the original code.
      You need to distinguish three cases, to make your approach work:
      1. bottom & top > 0 stays the same (though I would use low = bottom - 2 + bottom % 2 as that automatically gets you the last positive even number below bottom and 0 is excluded)
      2. Bottom ≤ 0, top > 0, needs their low adjusted to the nearest higher even number and negated: low = -(bottom + bottom % 2) this also covers the bottom = 0 case.
      3. Top ≤ 0, bottom < 0, flip the signs and top/bottom to determine low/high, then multiply the sum by -1

    • @SpaceMonkeyTCT
      @SpaceMonkeyTCT Год назад +1

      I like your thinking about not altering the I/O of the function because bottom == top might need to return 0 and in making use of n(n + 1)/2, however, in doing so you have also changed the I/O because nowhere in the code does it check for negatives, which might be a valid use-case. Like you said, we'd need to know more about the original application.
      You are totally correct that readability has a cost, but after spending a few years working with code written by people with a different focus, readability is more than just nice, it is a godsend.
      If performance is a primary concern, I would suggest keeping the original code in the unit test as a reference and to compare to the optimised code to ensure the I/O has not been altered or to document how it has been altered.
      Getting back to the point of the original video, less indentation, we can re-write your code (not tested, probably has mistakes):
      int sumEvenIntRange(long int low, long int high) {
      // Method based on formula for sums of integers to n = (n*(n-1)/2)
      // If n is even, the sum of all even numbers up to n = ((n*n)+2n)/4
      // This assumes low and high will both be positive, so:
      if (low < 0 || high < 0) {
      return 0;
      }
      // this should be the second change everyone makes! (function name first)
      if (low>=high) { // Condition from original code doubted by some
      return 0;
      }
      high -= ((high%2 == 0)? 0: 1); // high-even to be included is either high if even or one less if high is odd
      highEvens = ((high*high)+(high2; // compilers probably optimise multiply/shifting
      // don't calc low sum if not required
      if (low

    • @logaandm
      @logaandm Год назад +4

      @@SpaceMonkeyTCT Your assumption is incorrect. I tested all the code. I did not however test for negative integer input as you point out.
      Honestly, I was just mentally lazy to check the math to see if it would work for negative numbers. It does.
      Modifying to replicate the original code for both positive and negative input the code is now simpler:
      int calculate ( long int low, long int high) {
      if(low

    • @SpaceMonkeyTCT
      @SpaceMonkeyTCT Год назад

      @@logaandm I was also too lazy to check the maths for negatives, assuming it only worked for positives, good to know it works negative too. I still stand by my point of readability when it comes to inverting the outer if to return early and even if you don't like ternary operators or shifting, I find them equally readable (which is not very) :)

    • @SpaceMonkeyTCT
      @SpaceMonkeyTCT Год назад +1

      @@logaandm I ran some tests and looked at the assembler. Calculating the low and high is basically the same using ifs and using ternary, except the high ternary reduces to high &= -2; a neat trick! It rewrites ((high*high)+(2*high)-(low*low)-(2*low))/4 into ((high*high)+(high+high)-(low*low)-(low+low))>>2, which made me think of rejigging the maths to use fewer operators so my current code is now:
      int sumIntRange(int low, int high) {
      if (high > 2;
      }
      Which is a tiny bit more performant but not readable.

  • @tenthlegionstudios1343
    @tenthlegionstudios1343 Год назад +3

    Had a similar reaction to that same suggested youtube video. Love the content and comparisons between the languages!

  • @ВадимУшаков-х4в
    @ВадимУшаков-х4в Год назад +36

    Maybe someone in comments also spotted this bug in C code: if the top is equal to INT_MAX we will get an infinite loop cause after checking number

    • @AEF23C20
      @AEF23C20 Год назад

      топ и боттом проверяются "где то" выше, зачем у вас неимоверное стремление проверить и перепроверить всё и вся, что перепроверять не следует?
      входные параметры в функцию никогда и ни за что не должны проверяться в самой функции, все эти проверки должны быть [как минимум], в вызывающем месте который вызывает эту функцию
      и даже кандишен (топ>боттом) вкрай идиотичен, потому что он должен быть опять и снова "где то" выше, т.е.
      main() {
      иф(топ>боттом) кэлькулейт(боттом топ)
      }
      и даже это идиотично, ну потому что на самом деле проверка (топ>боттом) _уже_ где то _существует_, ессно на этапе создания структур например, именно там она должна быть
      а вы как обычно "стремитесь" к массовой идиотии, и проверяете всё и везде, однако же идиотия на то и идиотия, ну потому что полностью лишена логики
      в итоге все ваши идиотские проверки проверок от проверок над проверками только лишь выжирают батарейки и атомные электростанции, и толку с этого абсолютно нет, это банальное вредительство

    • @AEF23C20
      @AEF23C20 Год назад

      а что бы функция не выкидывала переполнение, допишите к функции банальный комментарий, когда и при каких условиях функция будет валиться

    • @p39483
      @p39483 Год назад +1

      Inclusive ranges: Just don't.

    • @brandongregori995
      @brandongregori995 Месяц назад

      Good catch. That case would never with the original code though, since the bottom has to be less than the top for the loop to be executed on the first place

  • @PhotonCompute
    @PhotonCompute Год назад

    Thank you, that was very interesting. I did not get involved in C++ back in the 90s because there were too many problems to make the transition from C which I had been using since the 70s, but now I can see that it is a time to take a fresh look at C++ and (more importantly) at Rust. Cheers.

  • @HexenzirkelZuluhed
    @HexenzirkelZuluhed Год назад +22

    I've recently found more and more similarities in rust and python, actually. This is how I'd express it in Python, below. Replace top+1 in the range, depending on intent. I had the "Nevernester" Video in my recommendations, too. I still think he makes some good points, especially for beginners.
    def calculate(bottom: int, top: int) -> int:
    return sum(n for n in range(bottom, top) if n % 2 == 0)

    • @sheep4483
      @sheep4483 Год назад

      this reminded me of an old code golf problem I solved, where I solved it something like this
      sum(range(bottom + bottom % 2, top + 1, 2))

    • @jimatperfromix2759
      @jimatperfromix2759 Год назад +1

      Imagine that: Python has the most elegant solution.

    • @climatechangedoesntbargain9140
      @climatechangedoesntbargain9140 Год назад

      i don't like the n for n in

    • @snippletrap
      @snippletrap Год назад +1

      @@climatechangedoesntbargain9140 how about sum(filter(lambda x: x % 2 == 0, range(bottom, top)))

  • @Bbdu75yg
    @Bbdu75yg Год назад +2

    Damn ,The haskell code looks so good

  • @hemmper
    @hemmper Год назад +3

    The best way to "never nest" this is to use a much faster O(1) algorithm instead of the O(n) ones presented:
    int calc(int b, int t){
    b = ++b>>11

    • @anon_y_mousse
      @anon_y_mousse Год назад

      Best version I've seen yet that matches the output of the original. I like it, even if I think the functionality of outputting 0 when the range is passed in the wrong order is wrong behavior. Even correctly handles negative numbers.

  • @ar1i_k
    @ar1i_k Год назад +3

    The entire video I was waiting for you to change the C example by replacing "number++" with "number+=2" and reduce O(n) "even" comparisons to O(1) (only first number).
    The final C++ example made me want to scream.
    At least in Rust the same thing looks nicer

  • @captainfordo1
    @captainfordo1 Год назад +19

    There are very clear ways you can make the C code less verbose while still maintaining its readability. I’m surprised you didn’t try to fix it, and instead jumped straight to C++.
    Edit: This is how I would write the function in C, and yes, I prefer this even to Rust.
    int calculate(int bottom, int top)
    {
    if (top < bottom) return 0;
    int sum = 0;
    for (int i = bottom; i

    • @anon_y_mousse
      @anon_y_mousse Год назад +3

      Better, but could be better still by ensuring the bottom is even then incrementing by two each loop. Even that could still be improved, and I'm kicking myself for not thinking of it first, but by realizing that there are exactly half as many evens, possibly minus 1, in the range between the two numbers. No need to even loop.

    • @zxuiji
      @zxuiji Год назад

      @@anon_y_mousse The point is to add up the values though, for example in 2 to 10 you get the result 30, in 4 to 10 you get 28 via the loop, I've not been able to think of a calculation that does not involve the loop to get those values, how would you get them then?

    • @anon_y_mousse
      @anon_y_mousse Год назад +2

      @@zxuiji Another poster already did the work so I'll post their solution: b = ++b >> 1 > 1

    • @biomorphic
      @biomorphic 5 месяцев назад

      This is the way the code should be written. Junior programmers should note the separation of the if statement, and the final return from the for loop, also how the sum initialisation is part of the block, because it is referred to the block. This is the way I write the code, and everyone should do sir. I am happy I am not the only one. This gives me hope. Well done.

    • @ElementaryWatson-123
      @ElementaryWatson-123 3 месяца назад

      Comparing trivial examples is useless. I always ask to provide equivalent implementations of generic vector in different languages. It's not a difficult task, but it touches a lot of basic stuff that programmers deal with every day, so it serves as a much better test.

  • @teclab2265
    @teclab2265 Год назад +1

    I'm only on 1:36 and the entitlement is off the chart.. "Is not what I wanted to see.." ohh boy..

  • @Arturo-Peredo
    @Arturo-Peredo Год назад +6

    I feel the C code itself could have some improvements:
    int calculate(int bottom, int top) {
    if (top > bottom) {
    int sum = 0;
    if (bottom & 1 != 0) bottom++;
    for (bottom; bottom

    • @MrZzzjjj
      @MrZzzjjj Год назад +1

      good refactoring.
      A further improvement can be:
      int calculate(int bottom, int top) {
      if (top

  • @jaysistar2711
    @jaysistar2711 Год назад +3

    I had similar thoughts about that video. He did make it better, and I can't fault him for making content that is helpful for someone who wasn't initiated to functional style.

  • @helio6839
    @helio6839 Год назад +10

    I’m guessing the bug is that if top and bottom are equal, the c/c++ code returns zero, while the rust code actually runs the filter/sum combo on that single int.
    Edit: I was right

  • @dwarftoad
    @dwarftoad Год назад +4

    Note that since sum is commutative (and associative) you could use std::reduce in C++ instead of accumulate and use one of the parallel execution policies, if so desired. However the optimizer can also automatically vectorize loops sometimes as well, especially dealing with integral types and clear operations like +, so it is probablydoing that for the C code or the loop based C++ code and maybe the ranges one too, depending on the specific compiler etc.

  • @TheItamarp
    @TheItamarp Год назад +3

    A "friendlier" option to the ternary operator which is going to be more readable for some people is to check the reverse of the if statement first, and "exit early", placing the evens sum logic after the if block. This gets rid of the need for the else block and leads to an almost entirely flat function...

  • @RogueShadowTCN
    @RogueShadowTCN Год назад +2

    I was never able to get into c/c++ but now I really enjoy rust. Rust is great, pretty, expressive and straightforward. I remember having much difficulty in c++ finding the "right" way to do the simplest things... And it was uglier the nicer the feature was ....

  • @peddfast
    @peddfast 2 месяца назад +1

    I took the liberty of rewriting the calculate function in C. By observing that we only sum even numbers we can use the mathematical formula for even sums, which lets us skip the for-loop entirely and lets us skip doing the modulo operation on every number in our range, going from an O(n) solution to an O(1) solution.
    int calculate(int bottom, int top) {
    if (bottom > top) {
    return 0;
    }
    if (top % 2 == 0) {
    top = top - 2;
    }
    if (bottom % 2 == 0) {
    bottom = bottom - 2;
    }
    return top / 2 * (top / 2 + 1) - bottom / 2 * (bottom / 2 + 1);
    }

  • @oakley6889
    @oakley6889 Год назад

    So i got recommended and started watching CodeAesthetic and love his style and explaination. Now its suggested you, its one big circle

  • @christophbritsch1074
    @christophbritsch1074 Год назад +13

    I would have loved some benchmarks at the end. Like does the original version run faster? Do the std functions introduce unnecessary complexity or are they faster?

  • @BeansEnjoyer911
    @BeansEnjoyer911 Год назад +2

    Awesome! Rust and Haskell love!
    Also saw that Never Nesting video and was also unimpressed. Not only that he ended up creating an off by one error when he inverted a conditional statement

  • @dwarftoad
    @dwarftoad Год назад +6

    Just looking at number of instruction generated is not always useful (unless you're trying to fit the code on a tiny PIC or something): first, the optimizer will try to inline functions and unroll loops (more in -O3 than -O2) which is great because inlining (and unrolling to some extent) makes a bunch of other optimizations possible, and in addition, in C++ you will get alternate code paths generated away from the normal code (kind of like having a separate, alternate function not next to the "hot path" of instructions in memory) for exception handling because they are expected to be unlikely but still possible; but the actual time (or instructions executed) in the case of no exceptions thrown will still be pretty small. Also tip when trying to look at the assembly code in Compiler Explorer to judge your code: use argc or argv or console input or random numbers or similar as dependent values (inputs) of the example code, otherwise the compiler/optimizer will just evaluate the code at compile time and the resulting program is actually just spitting out the constant results (even if the code for functions etc. is still there in the binary.)

    • @anon_y_mousse
      @anon_y_mousse Год назад +3

      Can confirm, at -O3 I've seen entire test suites reduced to printf( "%d
      ", result ); // though I did it intentionally, it was still funny to see.

  • @SpudMackenzie
    @SpudMackenzie Год назад +1

    This kind of refactor feels like such a revelation after watching all your APL videos. That whole thinking with algorithms thing.

  • @spacelem
    @spacelem Год назад +8

    I saw the same video, and I was also disappointed. It's been a few years since I wrote anything in C++, and I'm glad to that it's getting better at FP, as whether I'm coding in R (which hates loops), or in Julia (which is just lovely), it's a style that's a lot more pleasant to work with.

  • @rad6626
    @rad6626 Год назад +8

    Me watching this guy "refactor" nice and readable code into the most monstrous spaghetti code I've ever seen

  • @jongeduard
    @jongeduard Год назад +2

    Currently learning Rust, so great to see such a video about it. Absolutely great programming language.
    This is after years of experience with several C family anguages for me, with C# as my daily one, which is wordt mentioning for it's functional programming (especially LINQ) as well.
    Doing your example in C# the code looks pretty nice as well. The only 2 downsides are that for the range we still need to call the Enumerable.Range method instead of using the two-dot way. This is because the Range type behind the two dots in C# has not yet implemented the iterator (Enumerator) pattern today. And we also need the ternary operator solution for top-more-than-bottom check.
    Luckilly C# has a Sum method, but otherwise the Aggregate method was still an option for the accumulation solution (reduce operation).

  • @DavidAlsh
    @DavidAlsh Год назад +6

    Cool video, thanks for sharing. It's an interesting discussion; the transfer from imperative to declarative programming styles.
    As you mention in your video, the declarative syntax is less overwhelming once you become familiar with the tools available to you and that's where my biggest criticism of it lies.
    I have found that declarative code tends to have a negative impact on maintainability. The inability to use skills learned from other languages requires contributors to have knowledge of language-specific utilities, thereby increasing the barrier of entry and reducing the pool of viable contributors or increasing the time it takes for individuals to be productive.
    The declarative aspects of C++ are different to that of Rust, to that of Haskel, to that of JavaScript - but an "if" statement is an "if" statement everywhere you go.
    In my experience, boring and obvious code makes for maintainable and approachable code. While I, as a hobbyist, love tricky fancy code - it really sucks in a project setting.

  • @chanky1000
    @chanky1000 Год назад +3

    I read the last histogram to mean that the simplest no-nonsense C with the O2 flag is way more efficient than all the funky new-age stuff that came later

    • @marcossidoruk8033
      @marcossidoruk8033 3 месяца назад

      That seems the most likely but you shouldn't jump to conclusions that fast.
      More instructions doesn't mean slower, those extra instructions could be there for loop unrolling and vectorization which is what I guess happens in the O3 C.
      In any case, the only way to know wich one is faster is to benchmark it.

  • @jenreiss3107
    @jenreiss3107 Год назад +2

    I came from Haskell to Rust and I love it

    • @MisererePart
      @MisererePart Год назад

      Even as Rust developer i must agree Haskell solution is quite elegant here.

    • @jenreiss3107
      @jenreiss3107 Год назад

      @@MisererePart Haskell will be that way right up until you need to optimize. The gap between readable and optimized code in Haskell is probably larger than in any other programming language

    • @mskiptr
      @mskiptr Год назад

      @@jenreiss3107 Well, you can optimize code with rewrite rules and other stuff like that…

  • @dera_ng
    @dera_ng 3 месяца назад +2

    Code readability inversely proportional to number of lines 💀

  • @AndyQuinteroM
    @AndyQuinteroM Год назад +1

    It's uncanny how we get such similar youtube recomendations

  • @michaelmueller9635
    @michaelmueller9635 Год назад +2

    BEST THUMBNAIL EVER
    I always post in comments, that Rust is crossover of C/XX + Haskell + programmer experience of severe bugs.
    If you understand C/XX and it's struggles and pitfalls.
    If you understand Haskell and it's struggles and pitfalls.
    Then you understand why Rust is done the way, how it is designed to.

  • @emilien.breton
    @emilien.breton Год назад +5

    "I saw it first!"
    I was also disappointed and somewhat annoyed when I watched the original video. I'm glad you made this video to show what a proper refactor can look like.

  • @tuluppampam
    @tuluppampam Год назад +1

    In case anyone missed it, and apparently many did, the idea of the original C code was to have some actually functional code to show how to reduce nesting
    It was not meant to be the best way of implementing the operation (it isn't)
    It was not meant to be perfect (it isn't)
    It also isn't the fastest, nor the prettiest, nor anything else
    It is a good example for what it is meant to be
    It was meant to give an example that can be abstracted to other situations (something it accomplished very well)
    Go watch the original video if you actually care about what it originally meant, but if you wanna whine about its imperfections, go on

  • @professorgvd
    @professorgvd Год назад +7

    I'm sure this is overly pedantic but the `iota(start, end + 1)` feels bad when `closed_iota(start, end)` exists.

    • @code_report
      @code_report  Год назад +5

      I actually didn't know about closed_iota. However, it only exists in range-v3, not C++20 or 23 ranges. That being said, I did start using range-v3 when I introduced ranges::accumulate so I could have added it then. Thanks for putting it on my radar.

  • @Tabu11211
    @Tabu11211 Год назад

    I love it when I find a random video that replies to another random video I watched. I missed the old RUclips algorithm when I could see things that I had never seen before. this kind of reminds me of that

  • @epolpier
    @epolpier Год назад +3

    "Nothing better than C" - Linus Torvalds

  • @TomHultonHarrop
    @TomHultonHarrop Год назад +3

    One thing I didn't see mentioned in the video or in the comments so far is the matter of compile times. I did some quick tests locally using the 'time' utility on macOS, timing compilation of each version. Unfortunately compile times get gradually slower each time. The initial C and reformatted C++ version take approximately 0.06s to compile (M1 Macbook Pro, Ninja build), but by the time we get to the range-v3 accumulate (ternary operator) version, the code takes approximately 0.8s to compile. This means the original version compiles 13x faster than the rangified version (an order of magnitude). It's tough as I do really quite like the more declarative approach of the final code, but the cost (when magnified across an entire code base) is prohibitively high at the moment.

  • @michaelsondei4695
    @michaelsondei4695 Год назад +1

    look how beautiful it is using c# linq:
    int Calculate(int bottom, int top) => Enumerable.Range(bottom, top - bottom + 1).Where(x => x%2==0).Sum();

  • @frydac
    @frydac Год назад +11

    I prefer a guard clause over if/else or ternary in this case. I think it improves readability.

  • @CaptSimmons
    @CaptSimmons Год назад +9

    how did you create the animation bewteen slides? looks great

    • @code_report
      @code_report  Год назад +7

      I have a follow up video on this in a couple days 🙂

    • @CostaKazistov
      @CostaKazistov Год назад

      PowerPoint maybe

  • @tordjarv3802
    @tordjarv3802 Год назад +5

    I would be embarrassed to see either of those in my code, and so would Gauss. Here is what I would do (Rust version):
    fn calculate(bottom: i32, top: i32) -> i32 {
    (top/2 + bottom/2)*(top/2 - bottom/2 + 1)
    }
    Infact, I'm pretty sure that some C and C++ compilers (clang) would recognize the pure for-loop versions as the sum of all even numbers between bottom and top and replace it with the very formula I used and thus turning an O(bottom-top) function in to a O(1) function. However, when using all the "zero-cost abstractions" this would not be the case. Also, as someone that is trained from childhood with function composition being from left to right and not right to left I find the Rust version to be backwards (the Haskell version is alright).

    • @peezieforestem5078
      @peezieforestem5078 Год назад +6

      I do believe that the code is not meant to be taken literally, e.g. you might want to edit conditions and values later, it's just an example code, hence why the function is called "calculate" and not "sum_of_all_even_numbers". But yes, you're correct that this particular version is solved in a const time with a simple formula.

    • @tordjarv3802
      @tordjarv3802 Год назад

      @@peezieforestem5078 I know that it is an specific example. However, you seem to miss my point, that by using all those extra levels of abstractions, both the programmer and the compiler might miss trivial optimizations.

    • @peezieforestem5078
      @peezieforestem5078 Год назад +1

      @@tordjarv3802 I see your point, but you assume optimization criteria.
      Let's say we optimize for minimum code alteration for likely changing requirements while also maintaining minimal code size. Now, the solution you propose is not optimal, nevermind trivially optimal.
      Had you known in advance that the problem will always stay this exact way, you would be correct. But you don't, in fact, depending on your environment, you can be 90% certain that at some point your customer will barge in and tell you to redo everything.
      In this case, you optimizing for specifics of the problem is waste in the meta-sense, unless you wish to challenge yourself.

    • @tordjarv3802
      @tordjarv3802 Год назад

      @@peezieforestem5078 If the customer demands to redo everything, it will not matter how the code looks (because you have to redo everything, or maybe you don't agree what "everything" means).

    • @tordjarv3802
      @tordjarv3802 Год назад

      @@anserinus no those two are not the same, because we are talking about integer division. for example let top=5 and bottom=3 then (5/2 + 3/2)*(5/2-3/2+1)=(2 + 1)*(2 - 1 + 1)=6, while (5+3)*(5-3+2)/4 = 8.

  • @VimCommando
    @VimCommando Год назад +2

    Having spent some time programming Javascript and Ruby, starting in high school with C and Pascal, I always though “Rust is a high level language” was an odd statement. But after seeing that C++ comparison, now I get it 😂

  • @tylerdback2025
    @tylerdback2025 Год назад

    Very elegant code! Here's another reformat using the Common Lisp loop macro:
    (defun calculate (bottom top)
    (loop for x from bottom to top
    when (evenp x) sum x))

  • @glaux102
    @glaux102 Год назад

    this is the senior content i was looking for

  • @thenewchicken1339
    @thenewchicken1339 11 месяцев назад +1

    I’m not a professional at all but what I think is that the “never nester” RUclips video was going for code readability making it easier to understand. However I think ur going for something different. Not to say ones better than the other, but you don’t always have people who understand what you’re doing with lambda functions, iota, pipelining, etc. anyways all I’m saying is there’s pros and cons to everything

  • @DanelonNicolas
    @DanelonNicolas Год назад

    Ok. you got me. I just love this video! Wonderful to learn that rust improvement

  • @gardendado1999
    @gardendado1999 Год назад +1

    the datatype, we either need to limit the maximum sum of "sum" to an int32 or make "sum" > int32, int64/long etc

  • @mettemafiamutter5384
    @mettemafiamutter5384 Год назад +1

    I analysed the assembly code. At -O2 the C code has the tightest assembly and at -O3 the C code is auto vectorized. The C++ version cannot be auto vectorized and the scalar code that is generated at -O3 is H O R R I B L E compared to the C code at -O2. I wrote the scalar assembly by hand and compared it to GCC and I managed to shave off one instruction but that was it.

  • @eddi-y4e
    @eddi-y4e Год назад

    this is my personal refactor in C
    int calculate(int bottom, int top){
    int sum = 0;
    for(int number = bottom; number

  • @undofix
    @undofix Год назад +1

    I wrote some very short variation of this program in C. It may be incredibly ugly but it works :) If you could do this even shorter, I would be only glad to see!
    int calculate(int bottom, int top)
    {
    int sum = 0;
    for (int i = bottom+top*(top

  • @CamaradaArdi
    @CamaradaArdi Год назад +3

    Good video, but I'm not a fan of the "number of assembly instructions" benchmark. It would have been nice to have an actual time benchmark, which is not hard to do.

    • @MrNatsuDragneel
      @MrNatsuDragneel Год назад

      yes, sometimes less instructions can be take more time to process.

  • @fenandamilanda2032
    @fenandamilanda2032 Год назад +1

    7:50 even cooler you can use rayon to parallelize that workload with just a .into_par_iter() before the filter and sum

  • @Haskell-Curry
    @Haskell-Curry Год назад

    Congratulations, you just reinvented FP in C++

  • @patrickbarrett204
    @patrickbarrett204 Год назад +7

    I don't understand why I would want to use any of those C++ features for this task when the original C code is perfectly logical and readable.

  • @awawus
    @awawus Год назад +1

    C++ version was best at 4:36. You did C++ dirty by placing std::accumulate before iota and filter when the accumulate part is logically the last one. Rust and Haskell examples read very cleanly because they follow the flow of make an integer range, filter evens and sum, like a human language.

  • @markosth09
    @markosth09 Год назад +6

    Although rust is influenced by functional languages in general it is inspired by ocaml heavily. The first iterations of the compiler were written in ocaml.

    • @code_report
      @code_report  Год назад +1

      Yea, it would make sense that it is more influenced by OCaml due to the first versions of the compiler being written in it, but I have heard Rust folks say that Rust is Haskell + C++ so I'm not sure which was more influential.

    • @FlanPoirot
      @FlanPoirot Год назад +1

      @@code_report the only part of rust inspired by haskell directly was traits (typeclasses). you can look up graydon hoare's answer and you'll see that he said that Rust was mostly inspired by Ocaml and C++ (trying to remove it's footguns) not Haskell

    • @myxail0
      @myxail0 Год назад

      @@FlanPoirot and what about monadic ? operator

    • @FlanPoirot
      @FlanPoirot Год назад

      @@myxail0 that was only added later, in fact not that long ago it was a try operator and then changed to ?. so it might have gotten more features from haskell or other languages with stuff like that, I'm not qualified to answer that tho

    • @markosth09
      @markosth09 Год назад +1

      ​@@code_report Traits or type classes and rust’s error handling were very directly influenced by haskell. It sure has gotten a ton of features from c++ (without the footguns of course). All the languages mentioned influenced rust heavily, and I am not sure which one was more influencial either.

  • @keychera
    @keychera Год назад

    functional bros 🤝oneliner
    imma add
    kotlin:
    fun calculate(bottom: Int, top: Int) = (bottom..top).filter { it % 2 == 0 }.sum()
    clojure:
    (defn calculate [bottom top] (->> (range bottom (+ top 1)) (filter even?) (reduce +)))

  • @Pacanata
    @Pacanata Год назад

    Superb. I so enjoyed this video. Gracias!

  • @borisbeginin
    @borisbeginin Год назад

    Hey man great video! Really impressive c++ knowledge. However, it think the point of the original video was not about some particular function but more like an idea that the more branches it has the more bugs you will appear on average

  • @oj0024
    @oj0024 Год назад +6

    If the surrounding if can be omitted in rust, it can also be omitted in C, and probably in C++, but I'm not sure how iota is defined.
    Using the number of assembly instructions is a very bad metric, at least if you suggest less is better.
    I've created my own godbolt link under z eG84e44hr.
    If you look at the assembly generated from Rust and C, it's basically equivalent, and nicely vectorized with avx2.
    The C++ codegen is basically non optimized, and will be way slower.

  • @m4rt_
    @m4rt_ Год назад

    my preferred way of refactoring it is:
    int calculate(int bottom, int top) {
    if (top

  • @andregarvin5529
    @andregarvin5529 Год назад +2

    wow, you really took an okay piece of code and turned it into a hot mess. Clearly no junior dev will copy your poor mistakes

  • @bzboii
    @bzboii Год назад

    been loving the podcast rust content also that prob influenced this rabbit hole lol

  • @void_p
    @void_p Год назад +5

    wow I was looking for something like this, thanks!

  • @HarshVerma-oz5uo
    @HarshVerma-oz5uo Год назад +4

    Does this make the code execution faster or is it just about how fancy code can look?

    • @KohuGaly
      @KohuGaly Год назад +4

      I would be honestly surprised if there was any meaningful difference in performance, especially at high optimization levels. I'm actually surprised it even produces different assembly at all.

  • @watynecc3309
    @watynecc3309 Год назад

    CodeAesthetic is amazing

  • @jbolton1695
    @jbolton1695 Год назад +1

    A lot of the judgements made in this video are extremely subjective, and not much time is given as to **why** the examples are better, besides some sense of aesthetics, which is not the most important factor in engineering. I can give some concrete advantages of imperative code, such as: it's easier to run a debugger and stack trace, easier to extend the logic, and does not require as many libraries. When logic becomes less trivial, I would argue that straightforward imperative code is easier to debug via tracing and easier to estimate performance by seeing the total number of operations. Higher order functions hide a lot of what the computer is actually doing.
    While I agree that it can be very readable to write code that composes functions over iterables, chaining function composition is not always "beautiful" and can turn into a mess like any other code. It's going to be harder to go in and modify the logic within your loops to accommodate real world edge cases, such as breaking early on special conditions or keeping different accumulators. It's only going to be beautiful for very contrived examples like this one.
    I recommend looking up videos and articles by Casey Muratori and Jonathan Blow to get a different perspective.

  • @mert_2577
    @mert_2577 Год назад +2

    Honestly, looking at the C++ part, I can't really think who would write code like that. You don't need to use every standard library function or have a lambda for a command as simple as "return e % 2 == 0" since you use it once. I think the reformatting stage was more than enough because the rest is just overengineering at that point.

  • @duttaoindril
    @duttaoindril Год назад +2

    I think you missed the point of the Code Aesthetic Video - it was more about control flow than functional programming and algorithms. The other examples he had, where it was more business logic and things you can't simply reduce into better functional mathematical expressions, are where his tips are needed to have a more readable and maintainable codebase.

  • @zazahead
    @zazahead Год назад +2

    Even Java can be more concise here: BiFunction calculate = (bottom, top) -> IntStream.range(bottom, top + 1).filter(e -> e%2==0).reduce(Integer::sum).orElse(0);

    • @biskitpagla
      @biskitpagla Год назад +8

      that's got to be the ugliest piece of functional code ive ever seen

    • @IgnitionWeb
      @IgnitionWeb Год назад

      It's been a while, but I'm sure this style of Java and C#'s Linq expressions are slow. I would be surprised to see that in production code.

    • @zazahead
      @zazahead Год назад +2

      @@biskitpagla ugly, but it gets the job done.. like 95% of all java code

  • @muha0644
    @muha0644 Год назад +1

    my spin on this problem:
    ```int calculate(int bottom, int top){
    int sum = 0;
    bottom += bottom%2;
    while(bottom

  • @pfever
    @pfever Год назад +2

    11:30 So the code we started with was more readable and more efficient lol

  • @pskocik
    @pskocik Год назад +3

    I'd write the C code more like:
    int sumEvensFromBottomToTopIncl_On(int bottom, int top){
    int sum=0;
    if (bottom

    • @undofix
      @undofix Год назад +2

      Oh, that looks cool! Really the best one yet out here.

  • @ExodiumTM
    @ExodiumTM Год назад +21

    Man I'm still learning basic C stuff and that last C++ code is just ☠️☠️☠️

    • @Ogrodnik95
      @Ogrodnik95 Год назад +8

      im dev for years in cpp and it indeed is ☠☠☠ even for me. you dont use such things in code that many developers see and read every so often as to not confuse them. when writing code you try to write it in a way, that even dumbest but decently qualified dev can understand it. also names of variables and function arent what you would want to leave in source code. also, when it comes to extracting parts of code to separate functions - it is quite controversial - in my workplace we dont usually care enought to reduce function volume more as it is not that big yet and is decently uderstandable in current form. also, we dont really want to jump too much around the code to check each function due to size of project.

    • @not_ever
      @not_ever Год назад +1

      @@Ogrodnik95 You don't see this code in production but are you using C++ 20 or 23 yet*? Maybe this is a future we have to look forward to.
      * I agree with what you've written btw, this question is not a challenge to what you said.

    • @Ogrodnik95
      @Ogrodnik95 Год назад

      @@not_ever we 'just' started using cpp17 :D

  • @VictorMistral
    @VictorMistral Год назад +2

    For the assembly count, generaly smaller is faster, but not always, some asm instruction especially in CISC can be slower then other, (or faster).
    So if code path finds a way to use AVX it's probably be faster then one that use "jump" for the loop.
    And it's also possible that for some reason, one compiler targets more modern version of ix86 then another, or one compiler target different instruction, and add a runtime switch to select the best instruction set based of what is supported by the processor.
    O3 tends to favor speed over size, it probably unrolled the loop as much as it could to minimize the ammount of jump instruction that happened, and probably made all the jump as close as it could if it was all in a signe module to increase cache coherence of any jump along the hot path to avoid having to hit memory, and could thus be way bigger, and hopefully faster...
    So instruction count is "decent" metric, but far from enough... It's better if you target a specific RISC cpu, so cross-compiling everything a specific ARM version, makes instruction count more closely related to perf, in my experience...

  • @wut3347
    @wut3347 Год назад

    I currently learn C and when ever i see a C++ code my brain explodes, i hope when i get to C++ i will be able to understand this too

  • @ZakiWasik
    @ZakiWasik Год назад +4

    I started getting the CodeAesthetic videos recommended as well, so I had seen that one already.
    I like your approach using the built in helper functions, but I don't think that approach would have fit the CodeAesthetic video as those functions may not apply to other programming languages and he was providing a general example to be applied to any language.