17 Pieces of C# Syntax That Make Your Code Short

Поділитися
Вставка
  • Опубліковано 8 чер 2024
  • Become a sponsor to access source code ► / source-code-17-c-95476297
    Join Discord server with topics on C# ► codinghelmet.com/go/discord
    Enroll course Beginning Object-Oriented Programming with C# ► codinghelmet.com/go/beginning...
    The syntax of the C# programming language is changing. It has been changing since version 1, and we are still witnessing the addition of more details to it. Have you ever considered why we are getting these pieces of syntax and not some other?
    In this video, we will revisit a number of seemingly minor improvements added to the language over the years and draw them to a conclusion: Novel C# syntax makes writing pure functions easy.
    It takes time to accept pure functions as a design tool and a lot of practice to make the most out of them. But one thing I promise to you: Once you get there, you will never look back to the old-school imperative coding.
    And more: Your code will be way shorter than it used to be. How much shorter? 50-70% on average. That should motivate every programmer to start using the novel C# syntax as intended.
    ⚡️Chapters:
    ⌚ 00:00 Intro
    ⌚ 00:53 Imperative code (100% length)
    ⌚ 03:17 Object-oriented code (57% length)
    ⌚ 05:35 Comparing different styles
    ⌚ 06:45 Functional code (50% length)
    ⌚ 09:17 Pure functions (40% length)
    ⌚ 11:58 Conclusion
    Thank you so much for watching! Please like, comment & share this video as it helps me a ton!! Don't forget to subscribe to my channel for more amazing videos and make sure to hit the bell icon to never miss any updates.🔥❤️
    ✅🔔 Become a patron ► / zoranhorvat
    ✅🔔 Subscribe ► / @zoran-horvat
    ⭐ Learn more from video courses:
    Beginning Object-oriented Programming with C# ► codinghelmet.com/go/beginning...
    ⭐ Collections and Generics in C# ► codinghelmet.com/go/collectio...
    ⭐ Making Your C# Code More Object-oriented ► codinghelmet.com/go/making-yo...
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⚡️ Have a look at our other Videos :
    👉 Using GitHub Copilot to Write Complex Code | Step-by-step Tutorial ► • Using GitHub Copilot t...
    👉 Coding with GitHub Copilot - Beginner to Master | VS Code Demo ► • A Comprehensive Guide ...
    👉 What is Covariance and Contravariance in C# ► • What is Covariance and...
    How to Initialize a Clean ASP.NET Core Project with Entity Framework Core and Identity ► • How to Initialize a Cl...
    👉 The Null Conundrum: A Guide to Optional Objects in C# ► • How to Avoid Null Refe...
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⭐ CONNECT WITH ME 📱👨
    🌐Become a patron ► / zoranhorvat
    🌐Buy me a Coffee ► ko-fi.com/zoranhorvat
    🗳 Pluralsight Courses ► codinghelmet.com/go/pluralsight
    📸 Udemy Courses ► codinghelmet.com/go/udemy
    📸 Join me on Twitter ► / zoranh75
    🌐 Read my Articles ► codinghelmet.com/articles
    📸 Join me on LinkedIn ► / zoran-horvat
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    👨 About Me 👨
    Hi, I’m Zoran, I have more than 20 years of experience as a software developer, architect, team lead, and more. I have been programming in C# since its inception in the early 2000s. Since 2017 I have started publishing professional video courses at Pluralsight and Udemy, and by this point, there are over 100 hours of the highest-quality videos you can watch on those platforms. On my UA-cam channel, you can find shorter video forms focused on clarifying practical issues in coding, design, and architecture of .NET applications.❤️
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⚡️RIGHT NOTICE:
    The Copyright Laws of the United States recognize a “fair use” of copyrighted content. Section 107 of the U.S. Copyright Act states: “Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by reproduction in copies or phono records or by any other means specified by that section, for purposes such as criticism, comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an infringement of copyright." This video and our UA-cam channel, in general, may contain certain copyrighted works that were not specifically authorized to be used by the copyright holder(s) but which we believe in good faith are protected by federal law and the Fair use doctrine for one or more of the reasons noted above.
    #csharp #dotnet #functionalprogramming
  • Наука та технологія

КОМЕНТАРІ • 234

  • @FunWithBits
    @FunWithBits 55 хвилин тому +1

    This is a great example on 20 years of C# evolution. I actually used to write the code at the beginning (.net 1.1 days) and it brings me back. I'm here because it's hard to "change" how we code and our brains need continues education to stay on top of things. Its not just the "features", its how we approached it. The first part brought back to the early days. The middle part is where I am now. The last part is were I strive to be.

  • @Eltin123456
    @Eltin123456 5 місяців тому +113

    I would argue that code readability is far more important than reducing the code by 3, 5, or even 10 lines. Not to mention, that the code should be easy to debug. It seems like a challenge to make the code as short as possible which might be suitable for Codewars, but not for production code. So I would use "syntax improvement" that are really syntax improvements - that will show the intent and keep code readable and easy to debug.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +21

      I agree, but then I would add that mapping expressions are also more readable than the imperative code. Let alone that they are reducing bug count by an order of magnitude.

    • @lordicemaniac
      @lordicemaniac 5 місяців тому +4

      i agree, you are missing one point, the bonus way is also easily unit testable by very little and readable blocks

    • @anm3037
      @anm3037 5 місяців тому +4

      Readability and shortness do not oppose each other. Short codes are each to capture at one gaze; hence improving readability.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +10

      @@lordicemaniac Expressions are easier to test than imperative blocks.

    • @lordicemaniac
      @lordicemaniac 5 місяців тому +5

      @@zoran-horvat that is what i said, at least tried to... that the last bonus form maybe looks harder to read at first, but it is much easier to make very easy to read unit tests

  • @AlexUkrop
    @AlexUkrop 5 місяців тому +11

    I adore all your lectures, including on Pluralsight. You have truly changed my programming style/vision/life and my code is 10x less buggy than it was 10 years ago! Zoran Guru of Functional Programming! IoC + Interfaces + Linq + SOLID + Functional Programming (no loops, no deep branches, no cyclomatic complexity, only Linq and self-described small methods) == Success. Thanks a lot!

  • @pixelguy2231
    @pixelguy2231 5 місяців тому +12

    I like how your videos summarize most of the features from any C# versions, especially the new ones. I've seen the "when" keyword before, but never understood its use until now, same for using switches to declare variables.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +5

      Designers of C# are constantly watching what kind of code people write. Most of the additions to the language stem from practical needs and their net result is shorter code, i.e. the language saves us from repeatedly writing the same idioms.

    • @pixelguy2231
      @pixelguy2231 5 місяців тому +1

      @@zoran-horvat Interesting fact I see

  • @oysteinhaga
    @oysteinhaga 5 місяців тому +5

    As he says; F# is even more readable (preferable?). Here is a variant:
    let tryParse (input:string) =
    match System.Int32.TryParse input with
    | true, v -> Some v
    | _ -> None
    let produceSum (input:int seq) =
    match input |> Seq.toList with
    | [max; next; _] -> max + next
    | _ -> - 1
    let sumGreatestTwo (input:string seq)=
    input
    |> Seq.choose tryParse
    |> Seq.sortDescending
    |> produceSum

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      That removes at least a half of keys required to type the same code.

    • @user-tk2jy8xr8b
      @user-tk2jy8xr8b 9 днів тому

      Exactly same can be written in C#, modulo syntax

  • @fisnikmaloku3425
    @fisnikmaloku3425 5 місяців тому +3

    I watch your videos time to time, and I have to admit that each time I watch I appreciate more the content, because I understand the code and the use-cases where I would use these techniques.
    You're a great mentor, I've started learning from your videos in the beginning of my career, now I feel happy to have materialized that knowledge.

  • @luc9volts
    @luc9volts 5 місяців тому +2

    I like a lot your channel and your functional approach. I've been trying to do my code like this for years

  • @jonathanmccarthy3985
    @jonathanmccarthy3985 5 місяців тому +14

    Zoran Im a active Patreon subscriber and your videos are always helpful!. To be honest your videos make me uncomfortable (in a good way). It pushes me to find my gaps and progress my abilities. Thank you!

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +2

      Thank you for your support!

    • @jeremychristman1515
      @jeremychristman1515 5 місяців тому

      Your comment so perfectly describes how I feel also. I really value this mans content

  • @HOSTRASOKYRA
    @HOSTRASOKYRA 5 місяців тому +2

    Thanks! It was hard but very exciting!

  • @soverain
    @soverain 5 місяців тому +10

    I like the last example. I think the difficulty lies in the lack of understanding of the Aggregate method more than the code itself.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +5

      That was exactly my point. Once you switch your mind to seeing expressions, you start feeling odd when faced with something that is not an expression.

    • @okcharles7
      @okcharles7 5 місяців тому +2

      Aggregate
      (
      TAccumulator seed ,

    • @daaa57150
      @daaa57150 5 місяців тому +1

      I like the Aggregate method (i.e. "reduce" everywhere else) but I tend to not use it because let's face it, it's hard to read. And even if you master it, other devs reading your code will struggle.

    • @hemant-sathe
      @hemant-sathe 5 місяців тому

      To be very pedantic, we used the aggregate function but didn’t count the lines of it. Nevertheless, great example for moving from traditional object oriented to functional. Is there a video on map-reduce concept (not just the LINQ functions) in C# already by you? If not please make one. I have always struggled to recall it as it’s not used everyday.

  • @yufgyug3735
    @yufgyug3735 5 місяців тому +2

    very interesting video, i myself try to use some more modern syntax of csharp, it is usually an iterative process, balancing human readability, syntax and performance

  • @VladimirRoytman
    @VladimirRoytman 4 місяці тому +1

    Awesome techniques. Very educational. Thank you for your effort!

  • @dzllz
    @dzllz 5 місяців тому

    Your videos are very educational. Keep up the good work!

  • @_IGORzysko
    @_IGORzysko 2 місяці тому +1

    Great video Zoran! The last code presentation is not quite readable for me but I definitely would use functional programming due to its shortness and mutable resistance in general. 🤩

  • @RiversJ
    @RiversJ 5 місяців тому +1

    I do the FP styled code most of the time in my daily work, including using method groups as delegates (especially so even).
    My personal style is to do a bit less in expression bodied methods as i find the fellow who didn't write it lay struggle to read it, also still haven't taken the time to use all the Linq methods i could. Though my past experiences affect that as i used to write code where an IEnumerable was an ungodly memory hog very easily and Linq methods could easily box in not just the obvious variable but half a function chain. Learning off the aversion where appropriate (even the C#12 Linq simply doesn't hold a candle to careful proc code on blazing paths, but those are slow to write without bugs)

  • @ucretsiztakipci6612
    @ucretsiztakipci6612 Місяць тому +1

    Excellent content. I'm deadly curios about the performance between them.

  • @1Eagler
    @1Eagler 5 місяців тому +1

    It reminds me the old days of C. When you open a .h file, y will see one line with 120 characters ( in 80x25) and wonder what it does and how.
    Sorry, im too old for these: if I need more than 10" to understand a part of a code, this code needs either a comment or refactor it.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      Thirty years ago t'was an' it ain't no C these years no more.

    • @1Eagler
      @1Eagler 5 місяців тому

      @@zoran-horvat '87

  • @friedcrumpets
    @friedcrumpets 5 місяців тому +3

    I really enjoyed this video. It's a great overview of modern programming and oddly enough I'm currently looking into functional programming to see if I can snatch paradigms and bring them over to C#. My main question as a game developer... do I need to be concerned for performance employing such a style of programming?

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +2

      Game development has a large part that is performance critical, both in terms of CPU-bound operations and garbage collection. In that respect, most of the advice I gave in this video (and most other videos on my channel) is not good advice for game developers.
      On the other hand, all the techniques I show are tried and tested in business applications and services, where you can truly cut your codebase in half.

    • @soverain
      @soverain 5 місяців тому +3

      As a unity developer myself, I can assure you that you can effectively use some form of functional programming in game development. Sure making everything immutable and creating copies everywhere is not a good idea when we are working with large objects, but using pure functions in the underlying logic is largely feasible.

    • @Bankoru
      @Bankoru 5 місяців тому +3

      I use Option monads and Linq all the time in Unity. It is not a concern unless the profiler tells you so.
      Most of the time performance issues are in asset memory allocations (uncompressed textures etc)and loading said assets.
      Async functional code and Profiler are your friends.

    • @friedcrumpets
      @friedcrumpets 5 місяців тому

      @@Bankoru I've just had a google of Option Monads; something I've seen explained by here before and used before without realising.
      Thanks for the responses on this, actually really helpful 👍

  • @MasterKrzychuu
    @MasterKrzychuu 25 днів тому

    Nice, I forgot about Clamp but now I have the perfect idea where to use it in my code, thanks!
    Just saying, the way you've defined your Percentage class, it is still possible to initialize it with an invalid value: "var invalid = new Percentage(75) { Value = -12345 };". Change the init setter to private set to disallow this.

    • @zoran-horvat
      @zoran-horvat  25 днів тому

      Yes, that is the topic of other videos where I explain that issue.

  • @nickbarton3191
    @nickbarton3191 5 місяців тому

    I can honestly say that I'm writing code with pattern matching et al. Just the last step with delegates and Linq to make.
    Awesome Zoran.

  • @christensencm
    @christensencm 5 місяців тому +5

    The pure method is beautiful! I love the Aggregate method of boiling down an IEnumerable to a single output, it can be used for tons of use cases. My challenge is seeing the pattern. Moving from OOP to FP requires you look at the requirements from a different perspective. Sometimes, when I cannot see the FP solution right away, I will write it in OOP and then refactor until it is something elegant and pure. Thanks Zoran!

  • @aalasso2
    @aalasso2 29 днів тому

    I like this condensed abstracted format you have begun making where you iterate on a simple example. It really helps highlighting not just the "how" but the "why". My personal "why" has mostly been a feeling I have had for a few years, but that does not go far in persuading colleagues.
    Now, a few questions - some could be viewed as thinly cloaked critiques, but they are posed as questions since I might be missing something:
    1) In your final iteration, your parameter is repeatedly named `tuple`. Referring to your comment at 3:33 ("name after purpose, not type"), surely this should be `state` (or similar), no?
    2) In your switch expression, the second and fourth case differ only on `count` (`1`, `2`, respectively). Could they not be collapsed to a pattern matching on `(var max, _, 1 or 2)`?
    3) Your l.50 at 4:22 seems to be sacrificing security for brevity: Dereferencing `all[0]` and `all[1]` three times looks like a bug magnet to me. I would prefer a preceding line with `var (first, second) = (all[0], all[1])`, would you agree? Might there be a way to inline it, so as to get both brevity and security?
    4) While using pattern matching switch expression, I find it easy to get lost in the details because the patterns themselves cannot be abstracted away and given names. Surely switch patterns can be used in both "good" and "bad" ways. I would love to see this addressed in a future video.

    • @zoran-horvat
      @zoran-horvat  29 днів тому +1

      I agree with your comments. There will be some videos about the switch expressions and pattern patching expressions.

  • @johnsuckher3037
    @johnsuckher3037 5 місяців тому +4

    if I saw the last approach in a PR I would nope out

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +5

      So, your mind is not there yet. That is not a problem - keep learning.

    • @adambickford8720
      @adambickford8720 5 місяців тому

      @@zoran-horvat i do understand it... and i'd still reject it. Not because its bad code, but because i'd be the only one on the team that could maintain it, present or future.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +9

      @@adambickford8720 I was leading one such team and I have invested a lot in explaining this programming method. One of the members came to me a couple of years later to tell me that the team is still doing it that way, and that they would never trade it for their prior practices.
      I have never accepted the lack of education among the programmers I worked with. It is fine to not know something - there are tons of technologies I encounter daily and I don't know them. Not knowing is normal. But it is absolutely unacceptable to stick to sub par designs and refuse to learn better.
      I have done it through my career as a developer, as a team lead, and as a CTO - and it worked every time. But learning must be demanded explicitly. I never left it as an opt-in choice to my colleagues.

    • @adambickford8720
      @adambickford8720 5 місяців тому

      @@zoran-horvat we have had very different experiences!
      My employers have *actively resisted* that kind of solution because it requires 'rock star' devs and has too much risk. They can't afford "ivory tower" solutions that require additional ramp up time educating a team. They want the most fungible cogs possible as they expect the code to far outlive the team. That kind of code becomes some esoteric thing written by the greybeards of yesteryear nobody dares touch.
      TBC, it's incredibly frustrating to me the answer is to dumb down the solution instead of leveling up the devs. But the reality is I have to fight tooth-and-nail just to get people to use a 'reduce' properly. And when they report 'the PR works, but it's a hack' the boss will choose 'done right now' over 'done right' every time.

  • @AndersBaumann
    @AndersBaumann 5 місяців тому

    Hi Zoran. How to find a bug in the middle of a long, chained LINQ expression? Do you use the QuickWatch window or do you use another technique?

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      Why would you make a long, chained LINQ expression?
      If there are many operations to chain, I would expect to see them split into logical sections. If an operation makes a complex transform, I would expect to see that transform pulled out into a separate method or a function.
      The rules of managing complexity in LINQ are the same as in any other portion of code - don't let the code grow beyond a limit you can manage.

  • @patfre
    @patfre 5 місяців тому +1

    In the object oriented example you could reduce the curly braces to 0 if you remove the braces on the for each loops since c# counts the code in both of them as a single line so it is valid. And you also save some lines

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +2

      In a traditional code formatting style, that is usually considered an extreme and dangerous practice. I have nothing against it, personally.

    • @patfre
      @patfre 5 місяців тому +1

      @@zoran-horvat I also have nothing against it personally because of the fact that it’s in the way I format my code makes it easier for me personally to see what it does but I am also against it in certain cases like if else statements I wouldn’t do it but if it was just a single if statement I usually do it to make it simpler. I do also understand that it may not be the best I understand both sides here

  • @coderider3022
    @coderider3022 Місяць тому

    Late 20th century code - I’m going to borrow that line ! Problem is we get taught to do that then when we get a job, it’s hard to unlearn this.

    • @zoran-horvat
      @zoran-horvat  Місяць тому

      I know. I've been there, I've done that - for a decade at least.
      The good thing is that all this I am teaching exists in books. That is how I found about the problem through my career and then gradually improved.

  • @SirBenJamin_
    @SirBenJamin_ 5 місяців тому +1

    My worry with C# having so many different ways to do things in the same language is that when you have a shared code base with lots of different people, there will be lots of different "flavours" or styles and implementation techniques using different styles of the language ... i.e its going to encourage inconsistency. But then on the flip side, it makes the language so powerful and means it can be used in more places. So yes, I'm not entirely sure how I feel about the current direction of C#.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      It is common nowadays to split large domains into smaller components - services, vertical slices, all sorts of them. I don't expect different styles to flourish within a single component if it is small enough.

  • @okcharles7
    @okcharles7 5 місяців тому

    Happy new year!!
    Now, I can be free to use "when" clause in switch expression body and many thanks for great tutorial.
    Btw, it seems the seed values for the max and the next in functional variations should be int.MinValue instead of 0.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      No need for int.MinValue because there is count set to 0 to invalidate both values initially.

    • @okcharles7
      @okcharles7 5 місяців тому +1

      @@zoran-horvat you are right. Actually, what I thought was, those initial values could have made the switch case shorter, for example of Pure version:
      (var m, _, var c) when num > m => (num, m, c+1),
      (var m, var n, var c) when num > n => (m, num, c+1),
      _ => tuple with { count = tuple.count + 1 },
      However, after your reply, I found this rule could be applied to all the cases and your intention was to apply the same logic for all.

  • @victorcomposes
    @victorcomposes 5 місяців тому +1

    Wow, just beatuiful, thank you.

  • @Bankoru
    @Bankoru 5 місяців тому +1

    I love it, but my tech lead hates I even use expression bodied methods.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      I know of cases like that...

  • @jrandallsexton
    @jrandallsexton Місяць тому +1

    Mind. Blown.

  • @kostasgkoutis8534
    @kostasgkoutis8534 5 місяців тому

    Pretty solid code, i can tell that folding through the use of an FSM is kind of a pattern for you.

  • @1992jamo
    @1992jamo 4 місяці тому

    I have actually come back to this video, and honestly I it's incredible just how many fewer memory allocations there are with the functional approach. I ended up settling with this implementation which uses the same memory usage, but is easier for me to read.
    public static int SumOfTwoLargest(IEnumerable items)
    {
    var result = items
    .Select(item => { int.TryParse(item, out int number); return number; })
    .Aggregate((max: 0, next: 0, count: 0), (acc, number) => acc.count switch
    {
    0 => (number, acc.next, 1),
    1 => number > acc.max ? (number, acc.max, 2) : (acc.max, number, 2),
    2 when number > acc.max => (number, acc.next, acc.count),
    2 when number > acc.next => (acc.max, number, acc.count),
    _ => acc
    });
    return result.count == 2 ? result.max + result.next : -1;
    }

  • @antonknieriemen8682
    @antonknieriemen8682 5 місяців тому +2

    string[] nums= ["1","2","3","4","NAN","77","dude","23"]
    // modern syntax i prefer is 4 lines
    int sumlargest2 = nums.Select(x=>int.TryParse(x,out int i32)?i32:0)
    .OrderByDescending(i=>i)
    .Take(2)
    .Sum();

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      That makes an assumption of a small sequence of strings. Since that assumption is not listed in the requirements, your solution is not acceptable under the current requirements.

  • @muhammadumair9074
    @muhammadumair9074 3 місяці тому

    You discuss separate domain from infrastructure. My question is what are other names/concepts of infrastructure.

  • @avaygc5646
    @avaygc5646 5 місяців тому

    Can u make video about reflection and attributes im confused in that one

  • @varagor23
    @varagor23 5 місяців тому

    How good is the last "ulatimate" solution in terms of performance compared to the procedural approach? I see a lot of function calls there.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +3

      It is the job of a compiler, especially the JIT compiler, to inline those calls. You should train your eyes to view code through transforms and not through CPU instructions, mostly because anything you imagine will execute on a CPU is probably not true.
      It is indicative that either approach would accumulate to no more than 0.1% of execution time if execution includes loading the data from persistent storage, and to way under 0.05% of time if producing the response would include network transfer to a distant location.
      In other words, execution time is irrelevant. Development time, extensibility and bug count are the primary concerns for an engineer.

    • @theramblingbrit4431
      @theramblingbrit4431 3 місяці тому

      I was curious too, so ran some Benchmarks.
      In terms of performance, both "functional style" versions perform better than the more procedural styles. The "Functional" version seemsto consistently perform the best out of the 4, and personally seems the more universally readable of the two functional styles to me, so I'd go with that style personally.
      They also don't require any allocations unlike the more procedural versions, which tends to be the more common limitation I've come across.
      | Method | Count | Mean | Ratio | Allocated | Alloc Ratio |
      |--------------------- |-------- |------------------:|------:|----------:|------------:|
      | ProceduralMethod | 0 | 2.560 ns | 1.00 | 32 B | 1.00 |
      | ObjectOrientedMethod | 0 | 2.613 ns | 1.02 | 32 B | 1.00 |
      | FunctionalMethod | 0 | 1.275 ns | 0.50 | - | 0.00 |
      | PureMethod | 0 | 12.430 ns | 4.86 | 128 B | 4.00 |
      | | | | | | |
      | ProceduralMethod | 1 | 20.769 ns | 1.00 | 112 B | 1.00 |
      | ObjectOrientedMethod | 1 | 20.779 ns | 1.00 | 112 B | 1.00 |
      | FunctionalMethod | 1 | 13.387 ns | 0.64 | 40 B | 0.36 |
      | PureMethod | 1 | 25.185 ns | 1.21 | 168 B | 1.50 |
      | | | | | | |
      | ProceduralMethod | 10 | 128.382 ns | 1.00 | 288 B | 1.00 |
      | ObjectOrientedMethod | 10 | 137.218 ns | 1.07 | 304 B | 1.06 |
      | FunctionalMethod | 10 | 95.041 ns | 0.74 | 40 B | 0.14 |
      | PureMethod | 10 | 126.616 ns | 0.99 | 168 B | 0.58 |
      | | | | | | |
      | ProceduralMethod | 100 | 1,056.616 ns | 1.00 | 1256 B | 1.00 |
      | ObjectOrientedMethod | 100 | 1,121.603 ns | 1.06 | 1272 B | 1.01 |
      | FunctionalMethod | 100 | 903.564 ns | 0.86 | 40 B | 0.03 |
      | PureMethod | 100 | 1,040.499 ns | 0.98 | 168 B | 0.13 |
      | | | | | | |
      | ProceduralMethod | 1000 | 10,481.245 ns | 1.00 | 8496 B | 1.000 |
      | ObjectOrientedMethod | 1000 | 12,829.850 ns | 1.22 | 8512 B | 1.002 |
      | FunctionalMethod | 1000 | 10,251.213 ns | 0.98 | 40 B | 0.005 |
      | PureMethod | 1000 | 11,721.219 ns | 1.12 | 168 B | 0.020 |
      | | | | | | |
      | ProceduralMethod | 100000 | 1,378,139.537 ns | 1.00 | 1049162 B | 1.000 |
      | ObjectOrientedMethod | 100000 | 1,639,344.766 ns | 1.19 | 1049132 B | 1.000 |
      | FunctionalMethod | 100000 | 1,212,847.956 ns | 0.88 | 41 B | 0.000 |
      | PureMethod | 100000 | 1,321,638.203 ns | 0.96 | 169 B | 0.000 |
      | | | | | | |
      | ProceduralMethod | 1000000 | 17,178,708.152 ns | 1.00 | 8389238 B | 1.000 |
      | ObjectOrientedMethod | 1000000 | 19,084,339.955 ns | 1.11 | 8389254 B | 1.000 |
      | FunctionalMethod | 1000000 | 12,412,192.083 ns | 0.72 | 46 B | 0.000 |
      | PureMethod | 1000000 | 13,565,147.292 ns | 0.79 | 174 B | 0.000 |

  • @lee1davis1
    @lee1davis1 5 місяців тому +1

    Our brains must be programed to think this way through repetition.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      Precisely. I haven't met a programmer who didn't understand this process. But I have heard numerous team leads who say their team members cannot understand it. Those leads believe their colleagues are incompetent.

  • @DanielOliveira-hd9uc
    @DanielOliveira-hd9uc 5 місяців тому +2

    Nice approaches but, how about performance between each approach, which is better?

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +5

      What is performance when the data is loaded from storage, transformed, and then sent over the Internet? Whatever you write, and whichever coding style you adopt, the time your code takes will never approach one percent of the request-response time.

    • @conbag5736
      @conbag5736 5 місяців тому

      @@zoran-horvat While this is true, if you concede that this approach may not be suitable for something like Game development, and if you are advocating that people should adopt a more functional approach to their C# development - I think spending a minute or so being transparent about the performance of each approach would be welcome, even if it's negligible in 95% of use-cases.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +3

      @@conbag5736 The primary target of C# are business applications. Game development, system applications, embedded systems, operating system - you can do most of that in dotnet, but you should know on your own what tradeoffs each includes and how it differs from mainstream programming.

    • @Bankoru
      @Bankoru 5 місяців тому +4

      I ran a benchmark for the OOP, Functional and Pure cases. Surprisingly, functional had the best performance, followed by Pure (~18% slower), then OOP(~26% slower). In terms of memory allocation, OOP is surprisingly high (almost x1000 higher than functional in the 10000 strings case). I wish I could just post the table here.
      I find the pure case more interesting, but certainly less readable. Overall I'd stick to functional in this particular case (better performance/readability/memory) despite it not even using Linq (and it is faster specifically because it doesn't, although performance difference is negligible).

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      @@Bankoru The first two solutions collect the data into the list, and the last two don't - that is the difference in memory.
      Regarding CPU, I am not surprised to see the last two solutions coping well because all four solutions execute the same arithmetic operations in the same order, with only minor differences caused by the compiler optimizing them differently.
      Therefore, under the line, all that counts is the code structure on the screen, and that is where we can benefit the most from more expressive forms - functional design and expressions.

  • @Luke-me9qe
    @Luke-me9qe 5 місяців тому

    I would have thought you will put the tuples into using aliases. And I'm sure there is a reason why. Would you care to elaborate? Thx

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      Actually, I was thinking about doing that but eventually dropped the idea. Maybe I could play a bit with that in a separate video.

  • @MrFalcon58199
    @MrFalcon58199 5 місяців тому +6

    I like using these tricks to shorten my code, but I've noticed that sometimes it has a tendency to make the code less readable, so I try to balance those things

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +6

      Actually, the game is to train your eyes to see expressions, rather than statements. Once you get over that, you will start reading mappings and expressions fluently, and it will be imperative constructs and block statements that will hurt readability.

    • @JaconSamsta
      @JaconSamsta 5 місяців тому +4

      @@zoran-horvat
      Yes! In the end, it's all about conventions.
      One could easily argue that a for loop makes looping more difficult to understand, because all of the looping logic is contained in one piece of syntax and you need convention around how to interpret the syntax for it to be unambiguous.
      I for one am very happy to see more and more functional "conventions" make it into languages like C#.
      Yes, they may seem foreign at first and it takes time to integrate them into your own vocabulary, but they bring so much additional expressivity in the way they convey intent, it's almost crazy to think that I ever coded without them!

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      @@JaconSamsta Same with me, and my colleagues testify in the same spirit. Once you get there, you only turn back when there is justification: an express request for performance, an in-place algorithm, multipass algorithm, etc.

  • @dwhxyz
    @dwhxyz 5 місяців тому

    Another great video from Zoran! I do wonder if Microsoft are having ideas of merging C# and F# into a single language one day.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      I suppose not. Those are two views on software design.

    • @dwhxyz
      @dwhxyz 5 місяців тому

      ​@@zoran-horvat They are but with the rise in popularity of functional style programming along with what has been added to C# over the years has made me think there could be plans for this one day or to phase one of them out. I'm thinking quite some time in the future - 10+ years.

  • @PetrVejchoda
    @PetrVejchoda 2 місяці тому

    The fact that I can, doesn't mean I should.

  • @RicusNortje
    @RicusNortje 5 місяців тому

    I would love to see the performance for each method, for example the enumerator will add overhead when lowered to IL.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +2

      There is an analysis in the comments. Contrary to what many opponents of modern C#, it turns out that the two functional variants are the fastest. I expected that because I have seen it so many times already.

    • @sideshowfrost
      @sideshowfrost 5 місяців тому

      Hey, I had the same thought so tested it, and wrote about it here: blog.onelivesleft.com/2024/01/modern-c-performance-in-brief.html

  • @chrisspire
    @chrisspire 3 місяці тому

    The Discord link is broken - is Discord available for everyone?
    I love you content. Took me a long time to find someone with conceptual understanding and broad vision even though I am beginner at C#. There are a lot of simple solutions on TY but no one give answers to "why that desing? why that solution?". Big thanks!

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      Try now. I have replaced the link with a redirect link that should always be up to date.

    • @chrisspire
      @chrisspire 3 місяці тому +1

      @@zoran-horvat All good now. Thanks!

  • @logantcooper6
    @logantcooper6 5 місяців тому +1

    Great content

  • @TreeLuvBurdpu
    @TreeLuvBurdpu 4 місяці тому

    F# is a lot of fun.

  • @nickbarton3191
    @nickbarton3191 5 місяців тому +1

    Typed this code out and traced it in debug. Double awesome !
    About readability, is it possible to use an alias for the tuple ? The tuple structure is repeated half a dozen times and makes it look busy.
    I tried an alias but couldn't make it compile, needs C# 12.
    I converted to a struct using ReSharper and made it immutable but it needs a constructor, deconstruct; just ugly.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      I feared you would come with a bug report. I never ran that code while I was working on it :)
      All I know about its correctness comes from my trust in the development method I applied.

    • @nickbarton3191
      @nickbarton3191 5 місяців тому +1

      @@zoran-horvat It works great, even with a list of 1 or 0 items and also negative numbers.
      I'm just saying it's a little verbose with the repetition of the tuple.

    • @nickbarton3191
      @nickbarton3191 5 місяців тому

      Actually, I rewrote it using a SortedSet of the largest numbers instead of a tuple with another aggregate to sum them. IMHO, it's simpler and can be parameterized for how many max values to sum. Same line count.
      But I appreciate the demo of C# features, for me it's having the wisdom and experience to know how and where to use them instead of procedural style.
      I wonder also about performance, are there pitfalls to avoid? I need to prototype more before changing production code.
      Time is the enemy! In theory, I'm not allowed at the laptop in weekends 🙃

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      @@nickbarton3191 SortedSet removes duplicates. If you wanted to go that way, the SortedList is the right collection to use.

    • @nickbarton3191
      @nickbarton3191 5 місяців тому

      @@zoran-horvat Quite right, depends on the exact requirement I suppose but I didn't think about it.

  • @inzyster
    @inzyster 5 місяців тому

    Since I have a habit of benchmarking competing solutions for the same problem before picking one, I benchmarked each approach from this video. I tested 1, 5, 10, 100 and 1000 input items, all random from range (-4096, 4096) - using a fixed seed number for consistency. The last two methods win on memory allocation, which was constant (40 bytes on my machine) - so they scale best in that regard. The third method wins on performance, while the last one was sometimes twice as slow (even slower than the first two). Note that I ran this on a potato (2009 Macbook running Linux Mint), so the results may be different on something less ancient, but I'd expect the relative numbers to be just the same.
    So I guess the moral of the story is: BenchmarkDotNet is your friend :)

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      I plan to make a video on benchmarking, too. But you should be aware of the overall operation. Since each of the methods completes in microseconds, and data fetching that involves persistent storage would cost milliseconds, these methods are entirely irrelevant in the holistic performance analysis. Each variant is acceptable, so the decision is with maintainability, flexibility, and other -ilities that we need in software development.

    • @David-id6jw
      @David-id6jw 5 місяців тому +1

      I did some benchmarking as well. I tested list sizes of 10, 100, 10_000, and 1_000_000. I generated a random set of numbers (same set each run, generated outside the benchmarks so won't influence the runtime or memory scores) and stored them in an array (so minimal overhead on enumeration). .NET 8. Ryzen 3700 CPU. BenchmarkDotNet v0.13.12
      I think the most surprising was that MagicSum1 was slower than MagicSum0. These are basic syntactic sugar improvements, but MagicSum1 was ~5% (size 10 and 100) to 25% (size 10_000 and 1_000_000) slower than MagicSum0.
      Meanwhile, memory usage comparisons between MagicSum0 and MagicSum1 were a bit confusing. It varied between MagicSum1 using twice as much memory, to MagicSum0 using twice as much memory. Not sure why things varied like that.
      MagicSum2 and MagicSum3 were a vast improvement on memory allocation, using almost none regardless of data set size since they don't convert all the strings to a list of ints. However speedwise, MagicSum2 substantially outperformed MagicSum3.
      MagicSum3 was the slowest of all the algorithms for all data set sizes aside from 1_000_000, where it returned to being on par with MagicSum0. On the other hand, MagicSum2 was always the fastest, ranging from 10% to 20% faster than MagicSum0 (what I used as the baseline).
      For 1_000_000 data points, the difference between the worst (MagicSum1) and the best (MagicSum2) was 10 milliseconds (MagicSum2 being 35% faster than MagicSum1) and 13 MB of allocations. There was also a 5 millisecond difference between MagicSum2 and MagicSum3.
      I also tried making a variant which used MagicSum2 as a base, but using explicit if checks to see if the self-assignment of the default switch condition caused any additional performance overhead. The performance of the two variants was basically identical within the noise level, so the "syntactic sugar" penalty we saw going from MagicSum0 to MagicSum1 isn't hurting MagicSum2. Same when switching MagicSum3 to a generic variant.
      Overall, I'd say that MagicSum2 is the best for performance, memory, and readability. MagicSum3 is OK if you prioritize composability over the other metrics, and I think would also be preferable for generics (as long as performance isn't a major concern).
      So yes, go for modern code and pattern matching no matter what, but the step towards composed functional programming still needs additional thought based on your particular needs.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      @@David-id6jw Thank you for this input. You have got the point that the greatest advantage of the GetMagicSum3 is composability, which is favored more than speed in functional designs. However, its speed primarily depends on how the patterns are selected. I did literally nothing to help the compiler and that is the native result of letting the compiler do it all for us.
      The principal reason why we don't try performance optimizations in FP first is that the data crunching code is way faster than I/O anyway. Any function that requires I/O before and/or after data processing will take as much time as I/O dictates, with or without optimization of its CPU-bound part.

  • @vivekkaushik9508
    @vivekkaushik9508 5 місяців тому +1

    Brilliant.

  • @AlFasGD
    @AlFasGD 5 місяців тому

    The primary mistake here is measuring by LoC, which you very well reduce by including both the if statement and the code statement if the same line, which would otherwise be 2 or more lines. This greatly hinders readability, much like how the more "advanced" methods do. The point is not to write clever and short code, but readable and fast enough for your use case.
    The only thing I can commend for in this video is the showcase of C# features that everyone writing C# should definitely know. But returning magic tuples instead of declaring record structs for them comes against the point of the video, which is showing advanced C# features that people should be using in their code.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      I believe my point should have been made from the other end, though I'm not sure if I could communicate it well to the audience: _if_ you choose to design behavior in functional style, rather than object-oriented or procedural, _then_ you will have the novel syntax at your disposal and _that_ will help make your code shorter by a factor of two.

  • @robypeng
    @robypeng 5 місяців тому

    Hi @zoran-horvat, I'm one of many developers out there that are still struggling to understand and shift our comprehension and perspective into a functional paradigm. I'm also not the brightest one I would say. So my question is, how can we improve ourselves, make it familiar, and to get better in this topic?

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      Read everything you can grab. Learn every day. I have been doing that for the last 20 years and that has become my way of life ever since.

  • @Lazzerman42
    @Lazzerman42 5 місяців тому +3

    Impressive coding skills. Still, I would not recommend using that kind of advanced c# in an Enterprise project. Simply because all teams members won't have the same skillset - this type of code will be a paria that only one or two developers will dare touching. After 5 years it will be known as the code with technical debt.
    IMHO, writing the shortest/smartest code is something you do when you have a small dedicated team. In an Enterprise, writing easy to read code is king for long levity.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      I am trying to communicate the opinion that code based on expressions, pattern matching, and value mapping is also the simplest and easiest to understand.
      I have done that in my teams and, once you put things that way, members on the team accept it. You should not fear giving your colleagues an opportunity to learn.

    • @Lazzerman42
      @Lazzerman42 5 місяців тому

      @@zoran-horvat In my experience - things that are truly good, will be simplified by time. In other words, pattern matching syntax and functional program seems immature - but will probably mature and get better tooling support and "better" syntax if they bring business value.
      Happy new year!

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      @@Lazzerman42 I am sure these constructs will be even simpler in the future.

  • @rauberhotzenplotz7722
    @rauberhotzenplotz7722 5 місяців тому

    What about this approach?
    int SumOfLargestTwo(IEnumerable source) => source.Aggregate(
    (default(int?), default(int?)),
    (current, next) => current switch
    {
    (var x, var y) when x is null || next > x => (next, y),
    (var x, var y) when y is null || next > y => (x, next),
    _ => current
    },
    r => (r.Item1 + r.Item2) ?? -1); // ((x,y)) => (x+y) ?? -1 in the future

  • @antonknieriemen8682
    @antonknieriemen8682 5 місяців тому +2

    one line
    int sumlargest2 = nums.Select(x=>int.TryParse(x,out int i32)?i32:0).OrderByDescending(i=>i).Take(2).Sum();

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +3

      That algorithm requires O(N logN)) time and O(N) space. You cannot assume that is allowed unless requirements clearly state it is, e.g. by giving an acceptable upper bound for N.
      The solution from the video makes no such assumptions. It produces the result in O(N) time and O(1) memory.

    • @user-tk2jy8xr8b
      @user-tk2jy8xr8b 9 днів тому

      Almost, but it behaves incorrectly with non-numeric input mixed with negative numbers and doesn't return -1 if there are no two biggest

  • @allinvanguard
    @allinvanguard 5 місяців тому

    While code readability is an important aspect (Prefer understandable over clever code) - All those syntactical improvements are exactly why modern C# feels so much better than Java. Java is still almost stuck in the imperative phase, while we have a vast array of choices to pick from.

  • @luc9volts
    @luc9volts 5 місяців тому +1

    Awesome

  • @mdev3987
    @mdev3987 5 місяців тому

    Even though I follow the changes carefully I am getting a little bit worried about the direction of the language. Even though this is readable, and we implement somewhat a single responsibility principle, I would argue that the simplicity is getting lost in the syntax. But that’s just me.

  • @zumalifeguard3493
    @zumalifeguard3493 5 місяців тому

    Instead of a tuple, I would have used a locally defined immutable record. The tuple requires the definition to appear multiple times, increasing cognitive overhead unnecessarily.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      In many places in code it is not a tuple but an assignment to several variables.
      On the other hand, stepping from ValueTuple to a record class may require careful consideration of performance, especially if it is instantiated many times in a loop.

    • @zumalifeguard3493
      @zumalifeguard3493 5 місяців тому

      Good point with regard to performance.

  • @az6876
    @az6876 5 місяців тому

    Brilliant implementation!
    Can I give constructive criticism? Instead of using Aggregate, isn't the classic foreach loop better in terms of performance and readibility code?
    Something like this code:
    int MySumMethod(IEnumerable items)
    {
    (int max, int next, int count) tuple = (0, 0, 0);
    foreach (var item in items)
    {
    if (int.TryParse(item, out int number))
    {
    tuple = tuple switch
    {
    (_, _, 0) => (number, 0, 1),
    (var max, _, 1) when number > max => (number, max, 2),
    (var max, _, 1) => (max, number, 2),
    (var max, _, 2) when number > max => (number, max, 2),
    (var max, var next, 2) when number > next => (max, number, 2),
    _ => tuple
    };
    }
    }
    return tuple.count == 2 ? tuple.max + tuple.next : -1;
    }
    What do you think? Thank you

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      Once you see why Aggregate is better, you will never look back. Here is what makes it better: it _forces_ you to have an explicit transform.
      Without it, there is no barrier to stop your codebase from becoming 100% procedural, losing every single benefit you could get from having explicit small, composable transforms.

    • @az6876
      @az6876 5 місяців тому

      Hi @@zoran-horvat
      thanks for your reply. My criticism of the procedural whole is based not on the goodness of the code, which I do not discuss (on the contrary, I like it very much as in your implementation), but on the performance (and in my job I have to control that). If I do a trivial benchmark test between the Aggregate and foreach mode I proposed, I get these results:
      | Method | Mean | Error | StdDev | Gen0 | Allocated |
      |------------- |---------:|--------:|--------:|-------:|----------:|
      | GetMagicSum3 | 253.7 ns | 4.79 ns | 4.48 ns | 0.0253 | 40 B |
      | MySumMethod | 129.9 ns | 1.63 ns | 1.36 ns | 0.0253 | 40 B |
      This is one of the problems with this approach: apart from the readability of the code, which can be confusing for the inexperienced, the performance is often inferior (very high number of jumps, stack, etc...) and this is what still stops me in the all-procedural approach.
      At the moment I prefer a 'mixed' approach.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      @@az6876 You are discussing 0.1 microsecond where even the simplest database query or file request that would feed the data in takes at least 1ms. There is no point in optimizing one 10,000th part of execution time.

    • @az6876
      @az6876 5 місяців тому

      ​@@zoran-horvat no, it's not 1ms, but 40% less time. In a realtime context it makes a lot of difference.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      @@az6876 LINQ is not made for realtime applications, nor is C# for that matter, not even foreach.
      And, obviously, it is not 40% - for actual percentage, you must include the data fetching and result dispatch time. If that includes any I/O, as it does, then that 40% drops down to well under 0.1%.

  • @ClickOkYT
    @ClickOkYT 5 місяців тому +1

    Wow

  • @IvarDaigon
    @IvarDaigon 2 місяці тому

    Does the shortened code actually make it run significantly faster? If not then it's a waste of time because all you are doing is making it much harder to read, understand and ultimately maintain. This is why shorthand completely fell out of use, sure it was compact and efficient to write but almost nobody could read it.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      The final code is an expression. Your comment indicates that you prefer procedural code over declarative, and that is so 1980s. The sooner you rid that mindset, the better for you, trust me.
      There is a very simple empirical proof for that. I know many programmers personally who made that step and none of them ever turned back to say that expressions are not readable or something. That tells you should learn to favor expressions, too.

  • @lucifer-look-
    @lucifer-look- 5 місяців тому

    I am surprised such things are not obvious for people

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      You can see from the comments how many programmers fiercely oppose this style, holding on to the practices that are decades old.

  • @billy65bob
    @billy65bob 5 місяців тому +2

    I think I would've clumsily reduced it to about 6 Linq statements.
    Something like:
    var result = items
    .Select(n => int.TryParse(n, out var val) ? (int?)val : null)
    .Where(n => n.HasValue)
    .OrderByDescending()
    .Take(2)
    .ToArray();
    return result.Length == 2 ? result.Sum() : -1;

    • @spacepigs
      @spacepigs 5 місяців тому

      You should use .OrderDescending() and result.Sum()!.Value for the code to compile - it's probably the most concise functional solution.
      There's at least this interesting but rarely discussed factoid that C# automatically lifts operators to nullable which is why this approach works in the first place.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +4

      That takes O(N logN) time and O(N) space for an unknown N. Only if requirements guaranteed that N is small could we try this condensed approach.
      The requirements from the video are open to the possibility of working with extremely large inputs, and so I have implemented an O(N) time and O(1) space algorithm.

  • @hashemwannous374
    @hashemwannous374 Місяць тому

    Last two xd

  • @PeacefulMindss
    @PeacefulMindss 5 місяців тому +1

    😁 have no insightful comment other than the emoji, anyway, thank you man.

  • @banster85
    @banster85 5 місяців тому

    Really interesting, but i think it could also work without the need of handling the count variable simplifying the Advance method

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      The method must distinguish the case when there are less than two items in the sequence from other cases.

    • @banster85
      @banster85 5 місяців тому

      I mean something like this: return number > tuple.max ? (number, tuple.max) : number > tuple.next ? (tuple.max, number) : (tuple.max, tuple.next);
      This should work regardless the value of the count variable

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      @@banster85 But what about the case when there are no two numbers in the sequence?

    • @banster85
      @banster85 5 місяців тому

      ​@@zoran-horvatin this case the next variable will remain with its initial value 0 so the ProduceSum method can do the check on this variable, if zero returns -1 otherwise the sum​

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      @@banster85 What if the input number is zero?

  • @zimpoooooo
    @zimpoooooo 3 місяці тому

    The last version is unreadable to me :(

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      A matter of training the eye. Trust me, soon enough all code will look like that.

    • @zimpoooooo
      @zimpoooooo 3 місяці тому +1

      @@zoran-horvat Btw, great thought provoking videos, even if I may not necessarily adopt the style.

  • @j1shin
    @j1shin 13 днів тому

    So, a functional approach is presented by a functional advocate feeding functional programming fans. The outcome is pure hell for a non-functional programmer. I understand the reasoning but as some comments already stated, most programmers will struggle extremely with this approach.

    • @zoran-horvat
      @zoran-horvat  13 днів тому +1

      Name two non-trivial language features added to C# in the last ten years that are not either functional or performance. The C# language team is the greatest functional advocate in C#.

  • @Katniss218
    @Katniss218 Місяць тому

    I generally agree with this channel, but damn I really dislike how unreadable the last 2 methods are. Looks a bunch of mathematical mumbo jumbo.
    I'd personally settle somewhere between the 2nd and 3rd variant

  • @bonkers_dave
    @bonkers_dave 8 днів тому

    NOT a fan of counting things like curly braces or lines of code. The most important factor is how quickly root cause can be determined and how quickly a low risk fix can be applied when there is a production issue. It should be obvious to developers with average skills what the code is doing. The quest for "elegance" has caused more disasters than the quest for simple and obvious.

    • @zoran-horvat
      @zoran-horvat  8 днів тому

      What is the quest for elegance? Functional code is often so condensed that there is no place to put a big into. It is known in practice that bug count is by an order of magnitude lower in code based on pattern matching than in the corresponding procedural implementation. That is where counting curly braces suddenly becomes an important metric.

    • @bonkers_dave
      @bonkers_dave 8 днів тому

      @@zoran-horvat "elegant" usually means optimally condensed and optimally efficient. It can't be made more efficient and it can't be made smaller. The quest for elegance is the effort to achieve the absolute optimum state. With extensive experience in financial systems, I can attest to incidents when edge cases would produce failures that could be extremely costly. Real money, accumulating over time while an elusive bug is tracked down. Ability to readily understand code is the most important factor. Shorter is not always better. The problem isn't bug count, it is time-to-fix. Companies have gone out of business because of a bug that could not be fixed fast enough.

    • @zoran-horvat
      @zoran-horvat  8 днів тому

      @@bonkers_dave As I said, bugs you describe are by an order of magnitude more frequent in procedural code, for which majority of programmers still testify to be "readily understandable", although it is not.
      Your definition of elegance is, however, missing the point entirely. I don't know of a single programmer who condenses the code for the sake of condensing it.

    • @bonkers_dave
      @bonkers_dave 8 днів тому

      @@zoran-horvat "I don't know of a single programmer who condenses the code for the sake of condensing it." They are out there. I don't dispute any of your points in this video. I only point out that counting things like curly braces is not an important metric.

    • @zoran-horvat
      @zoran-horvat  8 днів тому

      @@bonkers_dave Counting curly braces in C# tells you what kind of a programmer you are looking at, because C# has been actively eradicating curly braces and substituting it with better syntax for the last 15 years. That is the underlying thought in my video.

  • @auronedgevicks7739
    @auronedgevicks7739 5 місяців тому +1

    The ultimate lesson here is don't write your own code, use a library written by someone smarter than you. Unless you want to be the person actually writing a library like that...

  • @oligreenfield1537
    @oligreenfield1537 5 місяців тому +1

    I will strike that code in PR review faster than light. It’s not the compiler to figure out the code It’s your F job.
    1 - Readability
    2 - Performance
    I swear to good I have enough to all of those who think that having performance issue is acceptable because you write business application.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      Performance? Oh, where have you been when another commenter has posted the results of measuring performance of the four methods? Guess what, the two functional methods are the fastest.
      Now you can take your preconceived opinions back. Better luck next time.
      Consider this a revenge of the compiler

    • @oligreenfield1537
      @oligreenfield1537 5 місяців тому

      @@zoran-horvat False the message made by the other user show that a foreach + matching pattern is 40% faster.
      My points are still valid. It you work in a large team where the code is handle by 3 or 5 person you have a issue of readability and skill level no everyone is interested by MS new sugar syntax.
      So I Will stay on my position I Will strike that code for corporate use but for you home project go head.

    • @zoran-horvat
      @zoran-horvat  5 місяців тому +1

      @@oligreenfield1537 How did you remove the part about performance from your opinion?
      You were very specific that this code is causing a performance issue that is somehow ignored because it is a business application. Now that it turned there is no performance issue, you pretend you never said it.
      My point is that your preconceived notion of a lacking readability is equally wrong, but you cannot see that from that 1990s trench in which you are dwelling.

    • @zzzzz2903
      @zzzzz2903 3 місяці тому

      ​@@zoran-horvat just a side - note, I feel 90's apps were much more stable and bug-free (and faster) than today's 😔

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@zzzzz2903 Actually, that is quite untrue. I know, I was there.

  • @adambickford8720
    @adambickford8720 5 місяців тому

    I would code this 'functionally' in java streams, which are kinda like linq if you squint. I tried to avoid int specific features like `sum()`, I may not have fully understood the reqs:
    var magicSum = Arrays.stream("421739".split(""))
    .map(Integer::valueOf)
    .sorted(Comparator.reverseOrder())
    .limit(2)
    .collect(collectingAndThen(
    toList(),
    twoLargest -> twoLargest.size() == 2
    ? twoLargest.getFirst() + twoLargest.getLast()
    : -1
    ));

    • @zoran-horvat
      @zoran-horvat  5 місяців тому

      The problem with this solution is that it requires O(N logN) time and O(N) space to complete, where there is no upper bound on N. The solution from the video runs in O(N) time and O(1) space under the same constraints.

    • @adambickford8720
      @adambickford8720 5 місяців тому

      @@zoran-horvat I'm going for something that feels straight forward to understand and maps to the problem in an 'obvious' way. This largely avoids the low-level imperative 'machinery' without being so abstract it doesn't readily convey meaning in this problem context.
      If there's an actual SLA we can certainly do something in our chain to achieve that.

    • @adambickford8720
      @adambickford8720 5 місяців тому

      @@zoran-horvat It looks like java added something similar to java 21 but, as usual, it seems more limited.
      I see what you're doing w/the count, it essentially acts as a state identifier vs explicitly having to match all of the parts in the pattern to infer state. It kind of reminds of using bit flags vs explicit booleans to manage things; clever, but not obvious.
      var patternMatching = Arrays.stream("32459722".split(""))
      .map(Integer::valueOf)
      .reduce(
      new TwoGreatest(0, 0, 0),
      (twoGreatest, number) -> switch (twoGreatest) {
      case TwoGreatest(_, _, var count) when count == 0 -> new TwoGreatest(number, 0, 1);
      case TwoGreatest(var a, _, var count) when count == 1 && number > a -> new TwoGreatest( number,a, 2);
      case TwoGreatest(var a, _, var count) when count == 1 -> new TwoGreatest( a,number, 2);
      case TwoGreatest(var a, _, var count) when count == 2 && number > a -> new TwoGreatest( number,a, 2);
      case TwoGreatest(var a, var b, var count) when count == 2 && number > b -> new TwoGreatest( a,number, 2);
      default -> twoGreatest;
      },
      (a, b) -> new TwoGreatest(Math.max(a.a, b.a), Math.max(b.a, b.b), a.count)
      );
      var sum = patternMatching.count == 2
      ? patternMatching.a + patternMatching.b
      : -1;

    • @adambickford8720
      @adambickford8720 5 місяців тому

      This avoids the O(N) space issue while still allowing for more than 2 items (does youtube block github links?):
      var limit = 2;
      var topN = stream("45432722557".split(""))
      .map(Integer::valueOf)
      .reduce(
      List.of(),
      (largestNFound, number) -> concat(largestNFound.stream(), of(number))
      .sorted(Comparator.reverseOrder())
      .limit(limit)
      .toList(),
      (a, b) -> concat(a.stream(), b.stream()).toList()
      );
      var sum = topN.size() >= limit
      ? topN.stream().mapToInt(Integer::intValue).sum()
      : -1;