10
\$\begingroup\$

I have been questioned about the byte count in my C# answer for the The Snail in the Well challenge. Usually C# answers only need to count the following bytes:

(a,b)=><do_something>

with the rest of the code being in the header or footer. So at first my answer was like this:

(a,b,c)=>a>b?1+f(a-b+c,b,c):1

Then Martin Ender noted that if I call a f method in my code, I should include that assignment in the code, so I changed it to

f=(a,b,c)=>a>b?1+f(a-b+c,b,c):1

Then Martin said that I should also declare the f variable and add the trailing ;, rendering my answer into something like this:

System.Func<int,int,int,int>f=null;f=(a,b,c)=>a>b?1+f(a-b+c,b,c):1;

At this point the answer is useless as there are shorter ways to solve the problem using standard, non-recursive functions.

Usually the declaration of the lambda expression goes in the header, that's why I left it there. But I understand this is a recursive function, so what should I include in the answer (and therefore in the byte count)?

\$\endgroup\$
5
  • \$\begingroup\$ Related. Related. Related. \$\endgroup\$ Jul 7, 2017 at 10:12
  • \$\begingroup\$ I have seen f=(<params>)=><do_something_recursive> many times... And I never saw someone complaining about it... \$\endgroup\$
    – Mischa
    Jul 7, 2017 at 10:29
  • \$\begingroup\$ Related (talks about one method calling another regarding lambdas, but can also be applied to recursive methods) \$\endgroup\$ Jul 7, 2017 at 11:57
  • \$\begingroup\$ @KevinCruijssen No positive answer or anywhere near a consensus there yet though. \$\endgroup\$ Jul 7, 2017 at 11:59
  • \$\begingroup\$ @TheLethalCoder I know. Just stating it's related. :) \$\endgroup\$ Jul 7, 2017 at 12:37

3 Answers 3

5
\$\begingroup\$

That structure is not self-contained

All the answers we expect must be self-contained.

The formulation f=<args>=><execution> is not self-contained.

Indeed, you can write

<type> f=<args>=><execution>

But C# doesn't allow the following construct:

foo.bar(f=<args>=><execution>);

This latter construct requires that f is typed before, like this:

<type> f;
foo.bar(f=<args>=><execution>);

So, while a lambda alone is self-contained, the structure f=<lambda> is not self-contained without the type of f, and is therefore not following the global rules of PPCG.

\$\endgroup\$
5
  • \$\begingroup\$ So in conclusion, it's better to not use lambdas when having recursion? So instead of System.Func<int,int>f=n=>n>0?doSomethingWithN:f(modifiedN); it's shorter to use int f(int n){return n>0?doSomethingWithN:f(modifiedN);}. And with Java it's not even possible to have recursive-lambdas, a.f.a.i.k. (not with java.util.function.Function nor with an interface). \$\endgroup\$ Jul 10, 2017 at 14:08
  • \$\begingroup\$ This answer only makes statements. I'm not saying if something is better or not. I'm only saying 2 things: the structure is not self-contained and the global rule for PPCG is "answer must be self-contained". Recursive is a tool, not the answer to life, universe and everything. \$\endgroup\$ Jul 10, 2017 at 14:57
  • \$\begingroup\$ I think your answer, or at least explanation, is better than the other and I do agree with you. I still feel like we should be able to do f=... but it seems consensus is against me here. \$\endgroup\$ Jul 10, 2017 at 15:05
  • \$\begingroup\$ This is a more targeted to-the-point address of the issue. We say the same thing from different angles but yours is much clearer. Part of that is that I'm a Java person :P You have my +1 (since a while ago actually) \$\endgroup\$
    – CAD97
    Jul 10, 2017 at 17:20
  • 1
    \$\begingroup\$ @KevinCruijssen Actually, it's possible to have lambda recursion in Java. But at the cost of putting it as a class member: class P{IntUnaryOperator f=x->x<2?1:x*this.f.apply(x-1);}, you can reduce the length with an interface, of course, but the big cost is the fact that you have to show it's a class member, and you have to use this. \$\endgroup\$ Jul 10, 2017 at 18:05
4
\$\begingroup\$

If I'm not mistaken, (my search has failed me), a unnamed function literal answer is defined along the lines of

a block of code that evaluates to a callable value

So, if your submission is

<code>

You should be able to do one of the following (structures) to run your answer:

<type> <result> = (<code>).call(<params>);

<type> <name> = <code>;
<type> <result> = <name>.call(<params>);

Or, as a function answer, adding your code should (in a language-defined way) provide a callable binding which solves the problem.

In Python, f=lambda ... works because after its execution, a binding f exists which is a reusable callable binding which solves the question.

In C-like languages, <type> f() { ... } works because after its inclusion in a source file, a callable binding f exists which is a reusable solution to the question.

In Java/C# though, we've run into the issue of typing with our unnamed function. As in the related issue about typing lambdas, we allow just the literal because the question itself gives the context to infer the required type of the binding, and it's more consistent with other languages' scoring.

However, I don't think this should extend to when the function needs to be named. At that point, you are no longer submitting an answer as an unnamed function, but as a block of code that after inclusion defines a function binding.

f=<lambda> means nothing in C#. You need to provide a type to create a name binding in C#.

I think C# has an opinion on this as well:

Cannot assign lambda expression to an implicitly-typed variable

I was going to suggest the use of var, but C# disallows that, likely because of the discussed issues about typing a lambda.

My position is that if you need to name the function submission, you need to provide a proper function submission, that is: code that, when included, defines a callable name binding which can be used to solve the question.

An unnamed function expression needs to be that: an unnamed function expression that I assign to a variable (and thus take the typing responsibility).

TL;DR this shouldn't be an allowed submission format, use a proper function declaration if you need to call it.

I agree with the summary in this answer, with the extension of allowing combining any number of defined functions with one anonymous function which is the submission.

TL;DR TL;DR: Oliver's answer expresses the same sentiment and I agree with it fully.

\$\endgroup\$
12
  • \$\begingroup\$ I understand your point of view, but then if non-recursive functions can be expressed as lambda expressions and recursive functions cannot, this penalises these answers so people will just stop writing recursive functions in C#, which can be in fact shorter and more elegant than their non-recursive counterparts. How then can we make the competition between recursive and non-recursive functions in C# a fair one? \$\endgroup\$
    – Charlie
    Jul 7, 2017 at 17:14
  • \$\begingroup\$ @CarlosAlejo unfortunately, though this does lead to less-competitive recursion, I still think this is the most consistent with other rulings. I really sort of wish var worked for lambda expressions, because then the answer would be pretty obvious. The only way I see to give recursive C# functions some ground back would be to allow var, either through a special-case ruling or the language's evolution. \$\endgroup\$
    – CAD97
    Jul 7, 2017 at 17:23
  • \$\begingroup\$ Taking into account that C# can only compete with other answers in C# (it is silly to try to compete against Jelly or other similar languages), what if C# just get its own set of rules? Do we really need to make the rules the same for every language? \$\endgroup\$
    – Charlie
    Jul 7, 2017 at 17:33
  • 1
    \$\begingroup\$ @CarlosAlejo all rules will be language-influenced: every language is different and thus required definitions of terms, etc. However, at least in my opinion, a stable "core" definition of a valid submission built upon those language-defined terms means a more approachable and understandable ruleset that is easier to use. \$\endgroup\$
    – CAD97
    Jul 7, 2017 at 17:36
  • \$\begingroup\$ @CarlosAlejo I'm playing here only in Java. Java doesn't support recursive lambda (yet). But even though, there are several cases where a plain recursive method works and is shorter than the iterative version. Latest example? I tried to write an iterative version of Pascal's Rhombus. Fail: the recursive was way shorter. I strongly prefer writing lambdas, yet this challenge forced me to write a full program. It was doable in a lambda, it was just shorter in that form. \$\endgroup\$ Jul 7, 2017 at 19:24
  • \$\begingroup\$ @CarlosAlejo Just consider the recursive way as a tool that you have to help you golf when necessary. And in regards to the question mentioned in OP, the iterative version is only 5 bytes longer. C# is not "competitive". If you want competitive switch to an eso language. If you want to keep having fun with C#, just accept it won't be competitive. \$\endgroup\$ Jul 7, 2017 at 19:27
  • \$\begingroup\$ @OlivierGrégoire of course C# isn't competitive, but it can be among other answers in C#, I just wanted recursive functions to have the same treatment as non-recursive ones. In the question linked, someone who writes the non-recursive version will win against the recursive one because of what CAD97 proposes, even though the recursive version should win as it is shorter to write. I hope this doesn't sound as a tantrum... :-) \$\endgroup\$
    – Charlie
    Jul 7, 2017 at 19:36
  • \$\begingroup\$ @CarlosAlejo I don't have any advice on the topic as I don't know C# enough. The main difference between a JS answer and a C# one is that C# is strongly typed and while it was always agreed lambda are accepted, a lambda is still self-contained. Can you write something like this in C#? x.accept(f=(a)=>a<0?1:1+f(a-1)) (for instance), or are you forced to write that using a separate variable? If you can write that, I guess there is no debate and it should be accepted. If you can't, however, it's not self-contained. \$\endgroup\$ Jul 7, 2017 at 19:58
  • \$\begingroup\$ Linq answers can also be more elegant, but the long method names make them longer than for(int i... answers \$\endgroup\$
    – Ewan
    Jul 8, 2017 at 9:09
  • \$\begingroup\$ LINQ is usually a question of whether it pays to add 'using System.Linq', typically a Linqy answer will be shorter excepting this (which you can't if you are using C# plain). It is not true to say that an untyped anonymous lambda in C# (e.g. x=>x.Length) is self contained, because the behaviour depends on the input types (and output types), and so is arguably meaningless without having some context for the compiler to assume the types from (and it sensibly assumes the most restrictive options). \$\endgroup\$ Jul 8, 2017 at 16:12
  • 2
    \$\begingroup\$ I'll never understand people's aversion to how we type lambdas, if _=>2 is allowed I see no reason f=_=>2 should not be. Both are equally undefined out of context so making one argument for one should apply to the other, yet people seem to be applying different arguments to the both of them. \$\endgroup\$ Jul 10, 2017 at 8:59
  • \$\begingroup\$ @TheLethalCoder See my answer for why it's not the same. \$\endgroup\$ Jul 10, 2017 at 14:59
3
\$\begingroup\$

If a lambda is to be called it should show what it is assigned to, so the following:

f=<args>=><someRecursiveWork>

If we view the code needing to compile this it looks like the following, note the trailing semi-colon:

System.Func<input...N, output> f = null;
f=<args>=><someRecursiveWork>;

However, we usually don't require trailing semi-colons for C# lambdas so I believe we should follow that rule here too.


On a side note the top comment should also be applied to if an answer uses multiple lambdas to delegate some work off:

g=<args>=><someWork>
<args>=><someWorkWithG>

Note how what would be f is not showing what it is assigned too.

\$\endgroup\$
2
  • \$\begingroup\$ I'm sorry, I disagree. \$\endgroup\$
    – CAD97
    Jul 7, 2017 at 16:54
  • \$\begingroup\$ This answer seems consistent with codegolf.meta.stackexchange.com/a/12747/8340. Both are hiding type information, but the non-recursive answer seems to better liked by the crowd. For now I'll stick with int f(int i)=>i<0?i:f(i-1); - but it would be cool to have this as an option! \$\endgroup\$
    – dana
    Jan 28, 2019 at 12:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .