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 Жыл бұрын
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 Жыл бұрын
i agree, you are missing one point, the bonus way is also easily unit testable by very little and readable blocks
@anm3037 Жыл бұрын
Readability and shortness do not oppose each other. Short codes are each to capture at one gaze; hence improving readability.
@zoran-horvat Жыл бұрын
@@lordicemaniac Expressions are easier to test than imperative blocks.
@lordicemaniac Жыл бұрын
@@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 Жыл бұрын
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!
@oysteinhaga Жыл бұрын
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 Жыл бұрын
That removes at least a half of keys required to type the same code.
@user-tk2jy8xr8b7 ай бұрын
Exactly same can be written in C#, modulo syntax
@pixelguy2231 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@zoran-horvat Interesting fact I see
@fisnikmaloku3425 Жыл бұрын
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.
@htspencer90844 ай бұрын
The syntactic information overhead increases dramatically between these implementations. I would argue that whilst they might seem simpler to you, many junior devs would struggle to parse this code at a glance. I really think it's a matter of who you expect to see the code.
@zoran-horvat4 ай бұрын
@@htspencer9084 I think you underestimate junior developers. It is actually the seniors who struggle.
@holger_p3 ай бұрын
It's easier to aquire habits, then it is to change habits. As more you know, as more complicated it's becoming for the senior to pick the right thing. And the worst thing to do, is dropping knowledge, forgetting about something.
@FunWithBits6 ай бұрын
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.
@jonathanmccarthy3985 Жыл бұрын
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 Жыл бұрын
Thank you for your support!
@jeremychristman1515 Жыл бұрын
Your comment so perfectly describes how I feel also. I really value this mans content
@jamilvillones502020 күн бұрын
This cannel is a masterpiece
@ultimStars28 күн бұрын
great video, my preference would be to second approach as the count variable seems extra just to get the pattern matching to work
@soverain Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
Aggregate ( TAccumulator seed ,
@daaa57150 Жыл бұрын
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 Жыл бұрын
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.
@antonknieriemen8682 Жыл бұрын
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 Жыл бұрын
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.
@yufgyug3735 Жыл бұрын
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
@sotsch92802 ай бұрын
Great Introduction to FC#! By trying to reduce the problem to most abstract way we also reduce lines of code and most important: State. Also readybility is opinionated, i find this easy to understand the more i read about it ther more it makes sense! Thanks alot!
@neniugrava4 ай бұрын
This video gave me flashbacks to reading through the Little Schemer book. I think many of us have stockholm syndrome for procedural algorithm implementations, because once you're familiar with the functional approach it seems crazy that you ever thought it was more difficult to understand 😂. I randomly got your channel in my feed and have loved all of the content (and I'm an embedded C programmer, lol). The way you explain things and the design of your examples is fantastic.
@luc9volts Жыл бұрын
I like a lot your channel and your functional approach. I've been trying to do my code like this for years
@ABMedia834 ай бұрын
Yep, Functional programming in C# really does speed up your development. I use both OOP and Functional programming in my applications. (A lot more functional and module programming)
@RiversJ Жыл бұрын
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)
@marwellus14 ай бұрын
A great and helpful lecture, thank you :) The first approach is so simplistic and kind of overly complex looking at the same time (to me) that I had to lough hard, but I remember this style from years ago which caused a ton of headaches (I almost dismissed C# entirely because of this). But at the end I've to say that I prefer the functional approach, it's just like a sweet little poem (after I figured it out myself how it works). The last pure variant, well, looks to "noisy" to me, by breaking it down even further into several "micro-methods", but it could make more sense if the problem you're trying to tackle is a bit bigger, so I keep that in mind for sure ;-) Subscribed, btw.
@MrFalcon58199 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@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.
@inzyster Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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.
@_IGORzysko8 ай бұрын
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. 🤩
@christensencm Жыл бұрын
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!
@nickbarton3191 Жыл бұрын
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.
@DanielOliveira-hd9uc Жыл бұрын
Nice approaches but, how about performance between each approach, which is better?
@zoran-horvat Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@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 Жыл бұрын
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 Жыл бұрын
@@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.
@patfre Жыл бұрын
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 Жыл бұрын
In a traditional code formatting style, that is usually considered an extreme and dangerous practice. I have nothing against it, personally.
@patfre Жыл бұрын
@@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
@antonknieriemen8682 Жыл бұрын
one line int sumlargest2 = nums.Select(x=>int.TryParse(x,out int i32)?i32:0).OrderByDescending(i=>i).Take(2).Sum();
@zoran-horvat Жыл бұрын
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-tk2jy8xr8b7 ай бұрын
Almost, but it behaves incorrectly with non-numeric input mixed with negative numbers and doesn't return -1 if there are no two biggest
@HOSTRASOKYRA Жыл бұрын
Thanks! It was hard but very exciting!
@1Eagler11 ай бұрын
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-horvat11 ай бұрын
Thirty years ago t'was an' it ain't no C these years no more.
@1Eagler11 ай бұрын
@@zoran-horvat '87
@NickMaovich2 ай бұрын
as a lifetime C# developer myself, this blows my mind
@zoran-horvat2 ай бұрын
It is important to follow the development of every programming language, so I believe. All programming languages change, and sometimes so significantly that they would be unrecognizable to an early developer.
@1992jamo11 ай бұрын
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; }
@Lazzerman42 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@Lazzerman42 I am sure these constructs will be even simpler in the future.
@kidmosey5 ай бұрын
One line of code would improve this by an order of magnitude. If the tuples were converted to a record type, it would be at least 10x easier to read, not to mention technically LESS code.
@Bankoru Жыл бұрын
I love it, but my tech lead hates I even use expression bodied methods.
@zoran-horvat Жыл бұрын
I know of cases like that...
@aalasso27 ай бұрын
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-horvat7 ай бұрын
I agree with your comments. There will be some videos about the switch expressions and pattern patching expressions.
@friedcrumpets Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 👍
@Cool-Game-Dev4 ай бұрын
My personal rule is to keep it out of update loops that run every frame, but this is ok in, for example, turn logic.
@TheAngpeu5 ай бұрын
Amazing! Keep going, Zoran.
@ucretsiztakipci66128 ай бұрын
Excellent content. I'm deadly curios about the performance between them.
@St4rdog2 ай бұрын
Would you use the C#12 'global using Something = (int Max, int Next, int Count)' here?
@zoran-horvat2 ай бұрын
@@St4rdog I would rather define a record for that. It is also one line of code, but it is more explicit.
@kostasgkoutis8534 Жыл бұрын
Pretty solid code, i can tell that folding through the use of an FSM is kind of a pattern for you.
@MasterKrzychuu7 ай бұрын
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-horvat7 ай бұрын
Yes, that is the topic of other videos where I explain that issue.
@billy65bob Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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.
@SudhanshuMishraTheOne6 ай бұрын
🤯in the best possible way! Hat tip to you sir 🙇, I’ll share your channel with my team. It is a goldmine!
@lee1davis1 Жыл бұрын
Our brains must be programed to think this way through repetition.
@zoran-horvat Жыл бұрын
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.
@muhammadumair907410 ай бұрын
You discuss separate domain from infrastructure. My question is what are other names/concepts of infrastructure.
@PetrVejchoda9 ай бұрын
The fact that I can, doesn't mean I should.
@robypeng11 ай бұрын
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-horvat11 ай бұрын
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.
@estardastrius93329 күн бұрын
There is still a major issue with your "Advance" function. Just looking at the patterns you are matching against, it is clear to see that there is a lot of duplicated logic or very similar logic. This makes it both hard to understand and errorprone to change. The underlying reason for this issue is that you are conflating two different operations with each other. The logic of the magic sum is to sum the two largest integers out of a list of stringst that may or may not represent integers. What you want to do to implement this is to a) ignore everything that does not parse to an integer b) gather at least two valid number items c) keep only the largest 2 number in cases where you have more than two numbers The pattern matching of the "Advance" function attempts to do b) and c) simultaneously. This is bad for maintainability due to the combinatorial explosion of pattern cases that would occur if the requirements would change e.g. to the sum of the largest 3 numbers. Separating the different processing steps, a straight forward "imperative" implementation could be something like this: int GetMagicSum(IEnumerable items){ var a = new List(); foreach (var item in items){ if(!int.TryParse(item,out int num)) continue; if(a.Count < 2){ a.Add(num); continue; } // else: keep only largest 2 items a = [Math.Min(a[0],a[1]), Math.Max(a[0],a[1])]; // Move smallest item to the left a[0] = Math.Max(a[0],num); // if smallest item is smaller than new item, it will be replaced } return a.Count == 2 ? a[0]+a[1]: -1; } This implementation also fulfills the requirements that are mentioned in other comments of O(n) time and O(1) space. (The O(1) space requirement is also not met by your first two implementations since all elements are gathered into a extra list, making them O(n) space.) The logic to update the collection of the two largest number has been reduced to 1+2 lines of code as opposed to the pattern matching implementation which requires 6 lines of code. It is also trivial to proof correctness since each step is either trivial or can easily be extracted into its own function for validation. Changing requirements from 2 to N items, the only major change would need to happen to the logic of keeping exactly N numbers. So before doing any change to syntax, one should first untangle their code and not attempt to do multiple data transformations at once
@VladimirRoytman11 ай бұрын
Awesome techniques. Very educational. Thank you for your effort!
@avaygc5646 Жыл бұрын
Can u make video about reflection and attributes im confused in that one
@RicusNortje Жыл бұрын
I would love to see the performance for each method, for example the enumerator will add overhead when lowered to IL.
@zoran-horvat Жыл бұрын
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 Жыл бұрын
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
@nickbarton3191 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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.
@nickbarton319111 ай бұрын
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-horvat11 ай бұрын
@@nickbarton3191 SortedSet removes duplicates. If you wanted to go that way, the SortedList is the right collection to use.
@nickbarton319111 ай бұрын
@@zoran-horvat Quite right, depends on the exact requirement I suppose but I didn't think about it.
@AlFasGD Жыл бұрын
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 Жыл бұрын
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.
@AndersBaumann Жыл бұрын
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 Жыл бұрын
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.
@jrandallsexton8 ай бұрын
Mind. Blown.
@varagor23 Жыл бұрын
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 Жыл бұрын
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.
@theramblingbrit443110 ай бұрын
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 |
@allinvanguard Жыл бұрын
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.
@coderider30228 ай бұрын
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-horvat8 ай бұрын
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.
@mkama-u5p10 ай бұрын
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-horvat10 ай бұрын
Try now. I have replaced the link with a redirect link that should always be up to date.
@mkama-u5p10 ай бұрын
@@zoran-horvat All good now. Thanks!
@NightKnight2526 ай бұрын
Thanks for showing me why F# is not more popular than many people hope it to be😆
@Luke-me9qe Жыл бұрын
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 Жыл бұрын
Actually, I was thinking about doing that but eventually dropped the idea. Maybe I could play a bit with that in a separate video.
@rauberhotzenplotz7722 Жыл бұрын
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
@holger_p3 ай бұрын
Maybe a word on what approach is easier to parallize would have been useful, or the topic of another video. A clean foreach most often is the easiest case.
@dwhxyz Жыл бұрын
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 Жыл бұрын
I suppose not. Those are two views on software design.
@dwhxyz Жыл бұрын
@@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.
@mdev3987 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
Good point with regard to performance.
@okcharles7 Жыл бұрын
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 Жыл бұрын
No need for int.MinValue because there is count set to 0 to invalidate both values initially.
@okcharles7 Жыл бұрын
@@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.
@az6876 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@zoran-horvat no, it's not 1ms, but 40% less time. In a realtime context it makes a lot of difference.
@zoran-horvat Жыл бұрын
@@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%.
@dzllz Жыл бұрын
Your videos are very educational. Keep up the good work!
@BigMaaaaaaan2 ай бұрын
Why do you make removing curly braces a goal? They make code more readable by clearly separating condition (if statement) from data manipulation. Without them we get a chaotic blob of characters.
@zoran-horvat2 ай бұрын
@@BigMaaaaaaan It is not a goal. An expression is the goal, and expressions have no curly braces. The farther you go in the direction of removing imperative instructions and transforming them into expressions, the more curly braces will disappear. Even the example you mentioned with the if instruction shows that effect. Why do you think in terms of ifs? It's a habit older than you. Start questioning it now.
@BigMaaaaaaan2 ай бұрын
@@zoran-horvat You mentioned it so many times that it seems to be a goal. The code you produced is elegant, but being squeezed into so little lines it becomes hard to read. The same could be achieved using curly braces, new lines for ternary operators, etc. It’s just a formatting remark, as I believe formatting impacts readability heavily. Other than that great code.
@BigMaaaaaaan2 ай бұрын
Also let me take this opportunity to thank you for your great content. I enjoy the way you talk about coding (both what you say, and how you say it 😀)
@zoran-horvat2 ай бұрын
@@BigMaaaaaaan It is not formatting. It is transforming statements into calculations. The entire programming has been moving in that direction for the last ten years. That process will not stop, and certainly not reverse.
@BigMaaaaaaan2 ай бұрын
@@zoran-horvat I agree, but that was not my point. However I am starting to realise what you meant by "removing curly braces" and it was and it was not what I thought you meant. I understood it more literally.
@TreeLuvBurdpu11 ай бұрын
F# is a lot of fun.
@victorcomposes Жыл бұрын
Wow, just beatuiful, thank you.
@zimpoooooo10 ай бұрын
The last version is unreadable to me :(
@zoran-horvat10 ай бұрын
A matter of training the eye. Trust me, soon enough all code will look like that.
@zimpoooooo10 ай бұрын
@@zoran-horvat Btw, great thought provoking videos, even if I may not necessarily adopt the style.
@IvarDaigon9 ай бұрын
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-horvat9 ай бұрын
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.
@Eva-km5ng6 ай бұрын
I love Haskell, I love Clojure, but the functional version of that code is unreadable. Way too clever. I would stick to the 2nd version, especially if I'm cursed to use C#. It doesn't really make sense to use the fanciest functional patterns in C# since the reason you're using C# instead of F# is most likely because your team doesn't want to learn functional programming. The last "best" way is the sort of code that convinces them that functional programming is too hard and not practical.
@Katniss2186 ай бұрын
13/18 is 72%, not 57%
@zoran-horvat6 ай бұрын
17/30 = 57% Just because 40% of vertical space are curly braces does not allow you to pretend they are not there. The 18 vs. 30 measure shows how effective that coding style is. It shows that 40% of vertical space is wasted with no effect on what the code is doing.
@vincentvogelaar60154 ай бұрын
Hmm. I now believe you should run for president. You are addicting to listen to🎉🎉🎉🎉
@duramirez5 ай бұрын
I have used the Functional approach to get data out of a PDF file that had specific data as label: data, and it was magnificent. I have made use of tuples all around, using a score counter to know when the label I just read word by word matches the label given from a parameter, and if so, then grab whatever was in front of the label as valid data up until the next label is found from a list of labels and return. My Junior developer saw that code and said: This is shit, chatGPT can do a better job, and it will not be this complicated and unreadable. That broke my heart 😞 I said to him: it looks complicated and unreadable, because you don't know what you are looking at, maybe learn more programming, and perhaps you can appreciate what I have done here. No need to depend on an API to do what I can do instantly with C#.
@zoran-horvat5 ай бұрын
@@duramirez Junior developers need to learn. For one thing, GPT in its current stage would probably make the whole bunch of subtle mistakes. I have made a few live demos showing how dangerous it is if one relies on GPT to write their code.
@duramirez5 ай бұрын
@@zoran-horvat Yes absolutely. He wanted to send the PDF file to GPT and it would return a Json with the data extracted as requested. But as you said, it could return whatever it wants or nothing at all, and there is still the fact that it is an external dependency call that adds latency and bugs. Oh well these Juniors these days hehehehe 😆
@zoran-horvat5 ай бұрын
@@duramirez Oh, that's what he meant? Hell, did he talk to the legal department before sending the PDF outside the company? :D
@duramirez5 ай бұрын
@@zoran-horvat I know right? 😆
@banster85 Жыл бұрын
Really interesting, but i think it could also work without the need of handling the count variable simplifying the Advance method
@zoran-horvat Жыл бұрын
The method must distinguish the case when there are less than two items in the sequence from other cases.
@banster85 Жыл бұрын
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 Жыл бұрын
@@banster85 But what about the case when there are no two numbers in the sequence?
@banster85 Жыл бұрын
@@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 Жыл бұрын
@@banster85 What if the input number is zero?
@j1shin7 ай бұрын
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-horvat7 ай бұрын
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#.
@rmcgraw79432 ай бұрын
i’d probably use Math.Max, and made all that one line.
@zoran-horvat2 ай бұрын
@@rmcgraw7943 What would you have used if max was not the answer?
@rmcgraw7943Ай бұрын
@@zoran-horvatHmm, a quasi-state machine like you did, but a manageable one. like i said, your code is good, great even, and you seem to be a great coder. As an programmer become enterprise architect, I still write code far more complex than this, even contributing to MS’s base class libraries, but there are few besides us that could grasp this code, much less modify it without F’ing it up.😅 You should dig into the BCL. IUd love it...type explosion to a new level. I recently created a X-plicit Arch template for my company, and would share if I could. A really great idea, and a good challenge for you, would be to create a configurable custom Partitioner for TPL. I partially wrote one a while back, and learned more about IEnumerable than I ever wanted to know. IEnumerable sux for TPL. Oh, one more thing. Got a paper, kinda old but good, you will love... PATTERNS OF PARALLEL PROGRAMMING by Stephen Taub Just search. It's a downloadable PDF. Stephen is one of those that could fathom your code, but he's unavailable to support it too. 😂 As a EA at an enormous organization, I sadly can’t be there to un-F every support issue, even though I would love to. 😪
@rmcgraw7943Ай бұрын
@@zoran-horvatYou should email Stephen. He does some videos for MS deepDotNet. They might would hire you. U have the knowledge and presentation skills.
@Katniss2188 ай бұрын
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
@vivekkaushik9508 Жыл бұрын
Brilliant.
@logantcooper6 Жыл бұрын
Great content
@bonkers_dave7 ай бұрын
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-horvat7 ай бұрын
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_dave7 ай бұрын
@@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-horvat7 ай бұрын
@@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_dave7 ай бұрын
@@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-horvat7 ай бұрын
@@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.
@margosdesarian5 ай бұрын
My brain bleeds
@JanVerny2 ай бұрын
I just don't like these high level abstracted transforms. In my eyes, the best code reads like a book and has structures that make identifying patterns easy (so no clever one liners). When people see messy cluttered one liners like the AdvanceRaw function their eyes just skip over. Mathematicians produce code that reads exactly like their boring papers. You need twenty mental steps to understand 7 characters. No one wants to do that for 8h a day. Also using tuples like this gets exhausting really fast. Not sure why anyone would choose not to just define a struct. But let's ignore the syntax, semantically it's a horror, AdvanceRaw is a nonsense function that just shouldn't ever exist. It does not describe an actual useful action, it only exists to satisfy ideological constraints and in the end complicates the path to understanding the actually important business/domain logic.
@zoran-horvat2 ай бұрын
@@JanVerny I see you are quite invested in denouncing all the C# syntax that is less than ten years old. Is there a system in that?
@AdamRichardz6 ай бұрын
This reminds me why i avoid "clever".
@PeacefulMindss Жыл бұрын
😁 have no insightful comment other than the emoji, anyway, thank you man.
@auronedgevicks7739 Жыл бұрын
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...
@CrapE_DM4 ай бұрын
I'm not a C# guy, but can't you do something like: var sorted = Items.Map(Int32::Parse).Filter((x) => x is not None).Map(Option::Get).Sort(reversed = true).Limit(2).ToList() if(sorted.length() < 2) return -1 else return sorted[0] + sorted[1] Clearly, you'd also need a helper function to change the parse function into one that returns an optional instead of the silly output parameter stuff
@luc9volts Жыл бұрын
Awesome
@hashemwannous3748 ай бұрын
Last two xd
@energy-tunes5 ай бұрын
why are you so afraid of curly braces
@zoran-horvat5 ай бұрын
@@energy-tunes Afraid?
@oligreenfield1537 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@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.
@zzzzz290310 ай бұрын
@@zoran-horvat just a side - note, I feel 90's apps were much more stable and bug-free (and faster) than today's 😔
@zoran-horvat10 ай бұрын
@@zzzzz2903 Actually, that is quite untrue. I know, I was there.
@adambickford8720 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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 Жыл бұрын
@@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 Жыл бұрын
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;