Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Spoiled by Perl

by afoken (Chancellor)
on Oct 31, 2021 at 01:10 UTC ( [id://11138265]=perlmeditation: print w/replies, xml ) Need Help??

Some context: My initial problem of making sense of a pile of Excel VBA junk (see [OT] Finding similar program code) is largely solved, and I have started re-implementing the calculations in C#. C# is obviously not my first language, neither the second or third one. It's more somewhere around number 20. I've lost count. Ranting about a certain Hunchback-Gollum-Salvatore C derivate caused a misunderstanding and triggered a reply by jdporter:

wait - do you mean C#? Because imho C# is a great language.

Jdporter is not alone, I've already discussed a lot with my coworker, who really likes C#.

The re-implementation is my first C# project, after a tiny "hello world" command line program just to test the VS2019 installation. I have already helped to debug some C# software, but rarely on the code level, more on the algorithm level, and of course some rubber duck debugging like "a < 10 looks wrong if you want to check that a is larger than 10".

C# is no huge surprise. It is just another language derived from C and some other languages, with OOP, and with its own set of quirks, strengths, and weaknesses. C# is designed for OOP, and supports OOP much better than some other langauges. OOP is not an afterthought, it is clearly part of the original design. Over the years, some syntatic sugar was added to the existing syntax, allowing to write quite short, but still readable code.

But I miss some really trivial things I'm used to in Perl:

The <=> and cmp operators together with ||

In C#, many classes (e.g. List<T>) implement a Sort() method that expects the C# equivalent of a compare function. You end writing something like this for a simple compare:

someList.Sort((a, b) => a.Foo.CompareTo(b.Foo));

Well, CompareTo() is not pretty, but still acceptable. I would prefer the (invalid) C# example below, perhaps using operator overloading to call CompareTo() behind the scenes. Unfortunately, C# has no <=> operator that could be overloaded.

someList.Sort((a, b) => a.Foo <=> b.Foo);

Compare to Find:

var first = someList.Find(e => e.Foo >= bar);

Compare to sort in Perl:

@someList = sort { $a->Foo <=> $b->Foo } @someList;

Things get ugly if you want to sort by more than one property:

@someList = sort { $a->Foo <=> $b->Foo || $a->Bar cmp $b->Bar } @someL +ist;

In C#, I ended up with that:

someList.Sort( (a, b) => { int rv = a.Foo.CompareTo(b.Foo); if (rv == 0) { rv = a.Bar.CompareTo(b.Bar); } return rv; } );

You can't use a.Foo.CompareTo(b.Foo) || a.Bar.CompareTo(b.Bar) in C#, because || is strictly boolean.

(Yes, I know that LINQ exists and has its own syntax plus extension methods to work with data in a way similar to SQL.)

redo

Restart a loop block, bypassing the conditional. See redo. It simply does not exist in C#. You have to use goto, which is at least ugly; or use an inner loop, which opens another can of worms (see below).

I rarely need redo, but some of the calculations need a redo. In VBA, it was simply implemented via goto jumping backwards over some hundred lines of code. My C# code also uses goto, for the lack of a better loop control.

next LABEL, last LABEL, redo LABEL

Again, those do not exist in C#. You can not get out of an inner loop AND start the next iteration of an outer loop, exit the outer loop, or restart an outer loop. Again, you have to use goto. If you need all three variants in a single set of two nested loops, you need three labels: One at the start of the outer loop to simulate redo, one at the end of the outer loop to simulate next, and one after the outer loop to simulate last. You could also break out of the inner loop and use a bunch of helper variables, which don't make the code more readable.

Again, I rarely need to control an outer loop from an inner loop, but some of the calculations need that, too. Again, VBA used goto and jumped wildly around, and my C# code also uses goto where I could not avoid it.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re: Spoiled by Perl
by eyepopslikeamosquito (Archbishop) on Oct 31, 2021 at 11:10 UTC

        ActiveState used to have tools that allowed you to integrate Perl and CLR based stull. The Perl itself was running using its own engine, but you could call one from the other. It makes more sense to do it that way anyway. CLR was not designed for dynamic languages.

        Jenda
        1984 was supposed to be a warning,
        not a manual!

      Thanks for the informative overview ...

      > claiming that Microsoft had exceeded the scope of its license by creating an enhanced version of Java that was fully operable only on Microsoft's operating system

      So same strategy like with JScript / IE to kill/steal the main project. Just that the story here led to ECMAScript.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        And sometimes good things simply vanish. I sadly remember this stuff which was my favorite waste of time as well as my cashcow for some years.

        «The Crux of the Biscuit is the Apostrophe»

Re: Spoiled by Perl
by holli (Abbot) on Oct 31, 2021 at 20:58 UTC
    I know the feeling. It's how people who learned Raku look at Perl: A good idea with sugar added later, but still leaves a lot to be desired.

    In Raku, if you were to sort on the result of a method you would just say
    @somelist.sort( *.Foo );
    This will even use a schwartzian transform if the optimizer decides it is worth it.


    holli

    You can lead your users to water, but alas, you cannot drown them.
      coincidentally I procrastinated this Sunday implementing syntactic sugar for exactly this in Perl5.

      There are some edge-cases tho, which keep we wondering ...

      In Raku:

      • how do you decide its a cmp or <=> ?
      • what if you want to apply another ordering? Like Czech sorting?
      • how do you sort by multiple criteria? Method chaining?

      > This will even use a schwartzian transform if the optimizer decides it is worth it.

      an optimizer detecting a good case for a Guttman-Rosler Transform transform would be more impressive tho. ;-)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        how do you decide its a cmp or <=> ?

        The dispatcher does that by finding the appropriate multi-sub that implements the actual comparison.

        # cmp. Generic, "smart" three-way comparator. multi sub infix:<cmp>(Any, Any) multi sub infix:<cmp>(Real:D, Real:D) multi sub infix:<cmp>(Str:D, Str:D) multi sub infix:<cmp>(Version:D, Version:D) # Compares strings with string semantics, numbers with number semantic +s, Pair objects first by key and then by value etc. # if $a eqv $b, then $a cmp $b always returns Order::Same. Otherwise O +rder::Less or Order::More.
        what if you want to apply another ordering? Like Czech sorting?
        Rakus Str class is fully Unicode aware and by default sorts characters on the numeric value of the codepoint.
        dd 'a ä b'.comb.sort; (" ", " ", "a", "b", "ä").Seq;
        If you wanted to implement your own behaviour you could just add another multi
        subset CzechString of String; multi sub infix:<cmp>(CzechString $a, CzechString $b) { # sort logic here }
        Or you can simply pass your comparator to sort
        sub czech-sort(String $a, String $b) { # sort logic here } @czech-words.sort( &czech-sort );
        how do you sort by multiple criteria? Method chaining?
        You can adress that in the comparator like in Perl, or make the comparator return a list of things.
        dd ([1,'z'], [2,'a'], ['1', 'a']).sort( *.[0,1] ); (["1", "a"], [1, "z"], [2, "a"]).Seq dd ([1,'z'], [2,'a'], ['1', 'a']).sort( { $^a[0] <=> $^b[0] || $^a[1] +cmp $^b[1] });


        holli

        You can lead your users to water, but alas, you cannot drown them.
      var sorted = somelist.OrderBy(x => x.Foo());

      But yeah, the woke butterfly that killed Perl wins on character count. The twenty years of waiting "paid off".

      Jenda
      1984 was supposed to be a warning,
      not a manual!

        The twenty years of waiting "paid off".
        Yes. Indeed it has. Check out this Raku code of mine which is a solution for this years day 14. Any experienced programmer should be able to make sense of, and mostly understand it after spending 2 hours with the docs. I doubt the same is true for any perl implementation of similar brevity.
        my ( \o, \r ) = .head.comb.List, .tail.comb( /\w/ ).map( -> $a, $b, $c { "$a$b" => [ "$a$c", "$c$b" + ] } ).Hash with cache $*IN.slurp.split: "\n\n"; sub day14( \n ) { my %d = o.rotor( 2 => -1 )».join.Bag; %d = [(+)] %d.map({ r.{.key} »=>» .value }) for ^n; .Bag.values.minmax.elems - 1 with ( o.tail => 1, |%d ).map: { .key.comb[0] => .value }; } say day14 10|40;


        holli

        You can lead your users to water, but alas, you cannot drown them.

        Nice. Complete version:

        var sorted = someList.OrderBy( _ => _.Foo ).ThenBy( _ => _.Bar );

        I think the above is produced by the following:

        var sorted = from item in someList orderby item.Foo, item.Bar select item;
Re: Spoiled by Perl
by jdporter (Paladin) on Nov 01, 2021 at 00:13 UTC

    Well, yes. It may be a great language, but that's in comparison to C++, Java, and other 3rd-gen languages - of which it is one itself. But it clearly ain't no Perl!

    I appreciate your very thoughtful writeup. Thank you!

Re: Spoiled by Perl
by ikegami (Patriarch) on Dec 07, 2021 at 14:27 UTC

    If it was a stable sort, you could use

    someList.Sort( (a, b) => a.Bar.CompareTo(b.Bar) ); someList.Sort( (a, b) => a.Foo.CompareTo(b.Foo) );

    But it's not. Another nice thing about Perl. (use sort qw( stable );, which is currently the default.) JavaScript also has a stable sort (at least since version 10 or EcmaScript 2019).

    That said, I like C#. It's quite good at doing what Perl does well.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://11138265]
Approved by kcott
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2024-03-28 22:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found