1. 7

What is a proper, refined way to use a loop to execute one event an even number of times, and another an odd number of times?

I know how to generate this string using a loop:

a,b,c,d,e,f,g,

But, if I want to generate this string, I have to alter the string outside of the loop. That is, I have to build it incorrectly then alter it. Or, I have to carry some flag into the loop from outside of it. Or, I have to feed a pre-mangled string into the loop. Over the years, I’ve used all of those methods, and I’ve been sure that there’s got to be a better way!

a,b,c,d,e,f,g

I get the feeling that implementations matter here, so… Off the top of my head, here are some languages that I’ve encountered this problem in… (I’m not saying that I know every nook and cranny or new development of any of these…)

  • C
  • Java
  • Bash (or KornShell if you prefer)
  • Common Lisp

Surely there is some technique available within that last one that I’m simply not aware of yet, but really, my question is more about the flow control loops found in the first three.

Thank you!

  1.  

  2. 5

    I think this is something I’ve encountered. But if I understood your clarification correctly, I think the way you phrase the question is really confusing.

    So instead of answering the question, I’m going to suggest a better formulation of the question and hope that helps.

    What you want to know is:

    How can I alternate between calls to two functions A and B a number of times (or until some condition is met) but skip the last call to B. For example ABABABA.

    An explanation of my (and possibly others’) confusion: The part about even and odd is misleading. ABABA (odd A, even B) and ABABABAB (even A, odd B) are both equally interesting to you.

    The part where A changes (abcd) but B (,,,) doesn’t is equally misleading.

    1. 1

      Yes, you understood what I was looking for. Sorry for those misleading parts.. Thank you for your assistance!

    2. 5

      You could use a generator that subsequently yields the next function. So, in Python ish:

      def cycle(*fns):
          while True:
               for f in fns:
                     yield f
      

      Then, you do the equivalent of take(cycle(one, two), N) and call each in a loop.

      1. 1

        Dang, I know all those words individually but I don’t understand the sentences you’ve built with them. :(

        I get a glimmer, though.. your cycle will give back either one or two.. Actually.. it gives back a never-ending stream of one-two-one-two-[…] ? I gather that python has ‘lazy evaluation’, then? (Or I have no idea what I’m looking at. :) )

        1. 1

          Yup! The idea is to build a lazy, never ending stream that cycles through the values you passed in. take in this case would build a stream that stops pulling values after N, from the cycle stream. Does that make sense?

          You can do this with a regular iterator in a language like C++. Your next() method advances state, for the next call to next()

      2. 4

        There’s a section in the Knuth essay around pages 18-19 where he gives some thought to “n and a half times” iterations which is what I see in your example:

        http://www.cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf

        I’m not really aware of a structure in your languages that makes it very simple without some repetition.

        I would probably use one of these patterns

         S; while not B do T; S; done
        
         while true do; S; break if not B; T; done
        

        where S is your data and T is the separator.

        1. 3

          Not sure if I get it. Here’s a quick hack that’s probably broken.

          #include <stdio.h>
          
          void a(void) { putchar('A'); }
          void b(void) { putchar('B'); }
          void (*p[2])(void) = { &a, &b };
          
          int main(void) {
              unsigned i, iterations = 7;
              for (i = 0; i < iterations; ++i)
                  p[i & 1]();
          }
          

          Expected output: ABABABA

          1. 1

            Neat, you made the two functions, made pointers to them, stuck the pointers in an array with members 0 and 1. Then in your loop, you do a bitwise AND against the iterator to get a 0 or 1 depending on the least significant bit of the iterator, and you invoke the corresponding function. You run the loop an odd number of times, thus running one of the functions once more often than the other. Cool.

          2. 3

            sh -c 'sep=; for f in {a..g} ;do printf "$sep%s" $f; sep=,; done; echo'

            1. 3

              Sure, this is an example of bringing state into the loop from outside: before your first time through the loop, sep=; #semi-colon is a character the shell takes so this is like sep=. After, sep=,, so printf gets the ,.

            2. 1

              After some discussion on IRC, I think I should add that this isn’t about the string. (Various “join string with some delimiter” functions exist.) It’s more about calling FunctionA an even number of times and FunctionB an odd number of times… So, maybe I build my string by calling AddNextLetter() and AddDelimiter(). Whatever.. :)

              1. 5

                Here’s my program:

                one();
                two();
                one();
                two();
                one();
                two();
                one();
                

                To me there are three ‘natural’ ways to factor out the repetition.

                ‘It is a call to one() followed by three calls to two(); one();’:

                one();
                thrice {
                  two();
                  one();
                }
                

                ‘It is three calls to one(); two() followed by a call to one()’:

                thrice {
                  one();
                  two();
                }
                one();
                

                ‘It is seven function calls, and the exact function depends on where we are in the sequence’:

                for i in 0..7 {
                  match even(i) {
                    true => one();
                    false => two();
                  }
                }
                
                1. 1

                  Ah, this is conceptually the same as @alva’s answer. And yours works in Java, too.

                  So, for either of these, I need to arrive at “7” programmatically. In my case, here is the expression that will achieve that: thing.length * 2 - 1. :)

              2. 1

                Why use a loop at all? In java: foo = Joiner.on(”,”).join(bar);

                My real answer is that high-level code is better than low-level code, unless there’s some really important justification for preferring a low-level implementation. That you know how to write the low-level code doesn’t mean that you should. What you should do is write lucid straight-line code (few or no indentation changes) and use abstractions to make that simple for yourself.

                1. 4

                  The real real answer is Json requiring that there must be no last comma causes a huge number of programmers a small twinge of pain each.

                  void damnJson( char ** string, size_t len)
                  {
                     if( len == 0) // You're always going to have to have a horrid corner case
                        return; // The early return pattern coupled with small functions equals cleaner code.
                  
                     // The Happy Path
                     fputs( string[0], stdout);
                     for(int i=1; i < len; i++) { // Note the offset by one start to loop.
                        fputc( ',', stdout);
                        fputs( string[i], stdout);
                     }
                  }
                  
                  1. 2

                    And that’s why language designers who care about the language’s “user experience” allow for a final trailing delimiter in lists. Perl does this and every DSL I design also does this, for exactly this reason. @arnt: I agree that using a higher level construct can often eliminate the problem. But sometimes (e.g. when streaming data), that’s not possible.

                    1. 2

                      And go mandates it, which is even nicer imo.