39 comments

[ 4.3 ms ] story [ 122 ms ] thread
Definitely reminds me of my highschool days. Except my gift was written in turbo pascal and could rotate around the Y axis without any flicker on a 486 DX. Drawing a heart on a TI-86 was too easy, so I had to up my game.
"Could I have that big red fluffy arse?" "Do you mean this plush heart?" "No. I'm a cardiologist, I've been operating on human hearts for the last 20 years, I know how a heart looks like, and I would very much like that red fluffy arse."

Seriously, though, it's a little heartwarming bouquet.

Ugh.

    for (i = 0; i < sizeof love / sizeof *love; i++)
        if (love[i] < 4)
            printf("%s", say[love[i]]);
        else
            for (j = 0; j < love[i] / 4; j++)
                putchar(show[love[i] % 4]);
Yes, C doesn't force you to use braces here, but this is really overdoing it.

And I say this as someone who does like Python's indentation-based syntax. Doing the same in C is not idiomatic.

That's the same style in K&R, which is about as "idiomatic" as it gets. I don't see anything wrong with it.
What's wrong with it is that it's really really easy to make a mistake and introduce a subtle bug that will be hard to spot. Especially now since languages like python have been developed that have made people used to indentation-defined code.

It's the style in K&R? Ok, so the literal inventor of the language was certain he wouldn't make this kind of mistake, that still doesn't mean I trust myself not to.

Omitting braces for single statements is quite idiomatic in the C development world, whether we like it or not. If we don't trust ourselves that we wouldn't make this kind of mistake, use the right tooling: 'gcc -Wall' (since GCC 6) or 'clang-format' can easily detect misleading indentation.
I don't have a copy of K&R handy, but I feel like I would remember if it really had an example like this nested three levels deep. I would be grateful if someone could check.
§ 1.5.3 Line Counting

  #include <stdio.h>

  /* count lines in input */
  main()
  {
      int c, nl;
      nl = 0;
      while ((c = getchar()) != EOF)
          if (c == '\n')
              ++nl;
      printf("%d\n", nl);
  }
§ 1.5.4 Word Counting

  #include <stdio.h>

  #define IN   1  /* inside a word */
  #define OUT  0  /* outside a word */

  /* count lines, words, and characters in input */
  main()
  {
      int c, nl, nw, nc, state;

      state = OUT;
      nl = nw = nc = 0;
      while ((c = getchar()) != EOF) {
          ++nc;
          if (c == '\n')
              ++nl;
          if (c == ' ' || c == '\n' || c == '\t')
              state = OUT;
          else if (state == OUT) {
              state = IN;
              ++nw;
          }
      }
      printf("%d %d %d\n", nl, nw, nc);
  }
Also, see § 1.6 Arrays, § 2.8 Increment and Decrement Operators, etc.

By the way, I would always use brace while working on a team project where there are other developers involved. But this is a one-time, personal, and hobby project where I am the only developer and I prefer to choose the K&R style when nobody else is involved.

Wow, that was quick, thanks. Though the first example is only two levels deep and also less complex than your code in question, which has a loop in the else part of an if in a loop...

The second example uses braces (as it must), so it's not an example of what I was asking for.

Okay, I was counting the nesting depth incorrectly. Here are a few more examples:

§ 3.8 Goto and labels

  found = 0;
  for (i = 0; i < n && !found; i++)
      for (j = 0; j < m && !found; j++)
          if (a[i] == b[j])
              found = 1;

  if (found)
      /* got one: a[i-1] == b[j-1] */
      ...
  else
      /* didn't find any common element */

§ 6.3 Arrays of Structures

  /* count C keywords */
  main()
  {
      int n;
      char word[MAXWORD];

      while (getword(word, MAXWORD) != EOF)
          if (isalpha(word[0]))
              if ((n = binsearch(word, keytab, NKEYS)) >= 0)
                  keytab[n].count++;
      for (n = 0; n < NKEYS; n++)
          if (keytab[n].count > 0)
              printf("%4d %s\n",
                  keytab[n].count, keytab[n].word);
      return 0;
  }
Thanks. I still find these a bit more palatable than your loop-in-an-else-in-a-loop (there are no elses and no loops inside ifs here), but yeah, I can imagine that K&R might have written it the same way as you did.
The second example does lack braces in two nested 'if' statements. Did you ignore that? Granted it is not three levels deep and granted there is a brace in the 'else' part but that's no reason to ignore the fact that K&R does in fact omit the braces for nested 'if-else' statements.

Demanding now that the nesting has to be less than three levels deep to omit braces is just moving the goalposts.

> nested 'if-else' statements

Yeah, "else if" is a special case where I wouldn't put braces even if I prefer them everywhere else.

> Demanding now that the nesting has to be less than three levels deep

I specifically asked for a three levels before, in https://news.ycombinator.com/item?id=19162933, not "now".

Your original critique from https://news.ycombinator.com/item?id=19161365 said this:

> Yes, C doesn't force you to use braces here, but this is really overdoing it.

It said nothing about nesting level depth. The question of nesting level appeared later in a child comment of yours which I believe is moving the goalposts. Sorry, I didn't make this clear in my previous comment.

Having said that, I do agree with you that braces are helpful. But then, we should all decide the pants policy in our own homes: https://groups.google.com/d/msg/nodejs/MWaivVTirPY/0pnRjKsgg...

Don't see how.

It's exactly as you might see in K&R, and short enough that there's never going to be confusion from their lack.

I used to code like that for a long time so it doesn't bother me too much. Then I started learning Rust (which uses a C-style syntax but makes braces mandatory ever for single statements) and I started doing it even in C. In hindsight I feel stupid for not doing it earlier, it doesn't add too much noise (at least if you put the opening brace on the same line) and it wards off many potential nasty bugs if you copy code around or use poorly written macros.
My rule has always been that if you're going to write a single line if-statement it should actually be a single line if-statement, i.e. no line break between the condition and the curly bracket.
I coded like this when I was self taught and then in college. Once I got my first programming job and started fixing other people's bugs that the braces would have avoided, I stopped doing this.
I'm sorry but I have to point out the irony in showing so much disgust about a few lines of code that happen to literally say "I love you."
The content is completely irrelevant, s/he only showed dislike of the indentation with which I disagree. What does it matter whether or not it says "I love you"? Should we not be able to criticize any other aspects of code when it prints or displays something positive?
Give criticism, sure, but keep some civility too. It's Valentine's Day! Be kind.
How was he not civil? Was it the "ugh" part?

Yeah, well... I am lonely. :(

And no unit tests or CI either? This is how rockets and hearts end up in pieces!

Good thing we can indicate our love with persnickety comments and have no need for such a program.

There is no love, only unit testing and coding style debates.
Hmm, I can have debates while maintaining my love for that person with whom I am having a debate!
(comment deleted)
The Python graph uses two equations.

y = sqrt(1 - |x|) * sqrt(|x|)

y = (-3/2) * sqrt(1 - sqrt(|x|))

Why do these equations work and look like heart? How does one make such equations?

I was expecting it to be the same algorithm in two different languages, but the Python version is a mathematical description whereas the C version is RLE-compressed.

Relatedly, there is this video on how to procedurally generate a 3D heart:

https://www.youtube.com/watch?v=aNR4n0i2ZlM

The two functions from Python one: y = sqrt(1 - |x|) * sqrt(|x|), y = (-3/2) * sqrt(1 - sqrt(|x|)). How do they produce the desired output? Is there a way to construct such functions?
In the README of the repository, I have mentioned a link to a five year old Reddit thread where I have discussed this in detail. Here's the reddit thread: https://www.reddit.com/r/math/comments/1xwuv3/a_mathematical...

Some of the "plot" links to WolframAlpha in that thread do not show the example graphs anymore, so let me rewrite that comment here with updated links. Rewritten comment with updated link now follows.

---

I wanted something that resembles the hearts in playing cards more than it resembles a cardoid, so I made a simpler heart for my girlfriend (now my wife) composed of two simple curves. The heart is plotted with two semicircles and segments of two quartic curves. Here is an explanation of how I arrived at these equations.

PART 1 - Plotting the Two Semicircles

First, we want to draw a circle of radius 1/2 centered at (1/2, 0). We start with an equation of a circle: (x - 1/2)^2 + y^2 = (1/2)^2 ⇔ x^2 - x + y^2 = 0 ⇔ y^2 = x(1 - x). Plot: https://www.wolframalpha.com/input/?i=y%5E2%20%3D%20x%281%20...

With the previous equation, we got both negative and positive values of y, hence a full circle. We want to eliminate the negative values of y, thereby resulting in a semicircle lying above the x-axis. We take square root of both sides: y = sqrt(x(1 - x)). Plot: https://www.wolframalpha.com/input/?i=sqrt%28x%281%20-%20x%2...

Now, we want a mirror image of this curve on the left side of the y-axis, i.e., for negative values of x, we want the same values of y that we see in the above plot. We do this by substituting x with |x| in the above equation: y = sqrt(|x|(1 - |x|)). Plot: https://www.wolframalpha.com/input/?i=Re%28sqrt%28abs%28x%29...

This completes the top portion of the heart with two semicircles.

PART 2 - Plotting the Segments of Two Quartic Curves

Now we try to complete the bottom portion of the heart. I didn't start with quartic curves. Instead, I began with parabolas and arrived at quartic curves. We begin with a single parabola: y^2 = 1 - x. Plot: https://www.wolframalpha.com/input/?i=y%5E2+%3D+1+-+x

We go through the same drill. We first eliminate negative values of y and then mirror the graph on the left side of the y axis: y = sqrt(1 - |x|). Plot: https://www.wolframalpha.com/input/?i=Re%28sqrt%281%20-%20ab...

Let us get a mirror of this plot below the x-axis by multiplying the right hand side by -1: y = -sqrt(1 - |x|). Plot: https://www.wolframalpha.com/input/?i=Re%28-sqrt%281%20-%20a...

But I wanted to get a little bit of concave curvature at the bottom of the curves to resemble the hearts in playing cards. I could get this by a little trick I learnt somewhere online: We can vary the power of |x| between 0.5 and 1.0 to get different curvatures, i.e., we can vary r in y = -sqrt(1 - |x|^r) where 0.5 <= r <= 1.0 to get different curvatures. I liked the concave curvature with r = 0.5, so I chose y = -sqrt(1 - sqrt(|x|)). This turns the segments of parabolas into segments of quartic curves. Plot: <...

Funny, elegant hearts