I was just given a challenge...

I was given access to a string (okay, character stream) through a function read() that returned each character of the string in turn. That was all I had. A sufficient mock function would be

function read()
{
	static $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
	static $position = 0;
	return $string[$position++];
}

You can see I didn't have much to work with. I was under even tighter constraints than that.

Here's what I had to do: output the string, but each time I encountered a semicolon, I had to reverse the character order of each word; tsuj ekil siht; until another semicolon reversed the reversal. Spaces and semicolons had to remain where they were (between and at the ends of words). There was also a full stop on the end: I was assured that the string would end with a full stop so that I could know when to, well, stop (since I couldn't know in advance how long the string would be). That had to be in the final output, too. Anything other than spaces, semicolons, or full stops could be considered part of the word.

For example, the result of mangling the above string should be:

This is a test; ti si ylno a tset; it is not the real thing; fi ti erew eht laer gniht uoy dluow ton eb gnidaer siht; you would be reading something else.

Those tighter constraints? I could only explicitly work with one character at a time. No substrings (of input or output or working space), no arrays, no character buffers, no random access of the source string. Read a character, deal with it, move on to the next. I had a while(true) { $c = read(); ...} loop and $c was the only part of the string I could refer to throughout the loop and I could only hold on to one "$c" at a time.

The final result isn't stored as a string, but output (one character at a time since you aren't building strings).

Before I post my solution, I'd like to see what others come up with. If the rules need clarifying, let me know.

Doubt I'll try to solve it, but...

Weedpacket Read a character, deal with it, move on to the next....
No substrings, no arrays, no character buffers, no random access of the source string.

Do you write the result to a string? If so, can you alter that string as you cycle through the input character stream?

I'm not sure I correctly understand all the constraints, but I couldn't think of any way to do this without accumulating words in a var.

<?php
function read()
{
        static  $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
        static  $position = 0;
        return $string[$position++];
}

$word = '';
$reverse = false;
$output = '';
while (true) {
  $c = read();
//  echo $c;
  if ($c == '.') {
    // current word finished
    $output .= $word;
    $word = '';
    // add c to output
    $output .= $c;
    // output finished
    break;

  } elseif ($c == ' ') {
    // current word finished, append it
    $output .= $word;
    $word = '';
    // add c to output
    $output .= $c;

  } elseif ($c == ';') {
    // current word finished, append it
    $output .= $word;
    $word = '';
    // add c to output
    $output .= $c;
    // flip reversing
    $reverse = !$reverse;

  } else {
    // some word character. append to word
    if ($reverse) {
      $word = $c . $word;
    } else {
      $word = $word . $c;
    }
  }
}
echo $output;
echo "\n";

Please let me know if I've violated anything. I could probably eliminate my $output var, but I can't think of any way to eliminate my $word var.

NogDog Do you write the result to a string? If so, can you alter that string as you cycle through the input character stream?

No, that would be a buffer. Or, to put it another way, holding on to more than one character at a time.

    sneakyimp I could probably eliminate my $output var, but I can't think of any way to eliminate my $word var.

    Yes, eliminating the $word var so that you're not building substrings is the hard part.

      I've added a couple of clarifications. You're generating output (à la echo $c) because you aren't building any strings; this includes working variables. If it weren't for the reversals it it could just be $c = read(); echo $c; if($c == '.') break;.

      It's not necessary, but you certainly can output while you're reading, just as long as the characters get output in the right order.

      Either way, the tricky part remains to control the order in which the characters are output; that is to say, the order in which those echo $c statements are executed.

        I think I've got it, but am currently occupied mooahahahaha.

          I tried two approaches, but I can't quite get it.

          Approach 1 involved writing using cursor control. I think technically this would be using the output buffer? Sadly, I can't get it to work because each time I do a left cursor, I don't insert the next char, i just overwrite the char that was to the left:

          function read()
          {
                  static  $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
                  static  $position = 0;
                  return $string[$position++];
          }
          
          function move_end() {
            echo chr(3);
          }
          
          $reverse = false;
          $cursor = 0;
          while (true) {
            $c = read();
          
            if ($c == '.') {
              // current word finished, go to end
              move_end();
              echo $c;
              // output finished
              break;
          
            } elseif ($c == ' ') {
              // current word finished, go to end
              move_end();
              echo $c;
          
            } elseif ($c == ';') {
              // current word finished, go to end
              move_end();
              echo $c;
              // flip reversing
              $reverse = !$reverse;
          
            } else {
              // some word character. append to word
              if ($reverse) {
                // cursor left
                echo "\033[D";
              }
              echo $c;
          
            }
          
          }
          echo "\n";

          The output:

          This is a test;tsyat; it is not the real thing;fteelgudtegs; you would be reading something else.

          Approach 2 I tried recursion but I can't quite get my head around it:

          function read()
          {
                  static  $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
                  static  $position = 0;
                  return $string[$position++];
          }
          
          $reverse = false;
          
          function echo_next() {
            global $reverse;
          
            $c = read();
          
            if (!in_array($c, ['.', ';'])) {
              // some word character. append to word
              if ($reverse && $c != ' ') {
                echo_next();
              }
              echo $c;
          
            } elseif ($c == '.') {
              echo $c;
              // output finished
              throw new Exception('output finished');
          
            } elseif ($c == ';') {
                echo $c;
                $reverse = !$reverse;
          
            }
          }
          
          while (true) {
            try {
              echo_next();
            } catch (Exception $e) {
              break;
            }
          }
          echo "\n";

          The output:

          This is a test; ti si ylno a;tset it is not the real thing; fi ti erew eht laer gniht uoy dluow ton eb gnidaer;siht you would be reading something else.

          You'll note it doubles up the spaces and wrongly reverses the first word sometimes. I give up. This hurts my brain.

            Well, I'll see if @NogDog wants to have another crack at it. Me, I ended up with lots of functions to "output the letter then the rest of the word" and "output the rest of the word then the letter", for various values of "word" and "letter", all calling each other to handle "the rest of the word".

            Weedpacket Well, I'll see if @NogDog wants to have another crack at it.

            Nah...too abstract and confusing to capture my interest, I'm afraid. 🤷

              Sure thing. Not enough regulars here for a proper coding challenge these days.

              So, without further ado, here's my solution:

              function read()
              {
              	static $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
              	static $position = 0;
              	return $string[$position++];
              }
              
              function output_generator()
              {
              	$gndn = function() { return; yield null; };
              	$word_generator = $gndn();
              
              	$reversing = false;
              	while(true)
              	{
              		$c = read();
              		switch($c)
              		{
              		case ' ':
              		case ';':
              		case '.':
              			yield from $word_generator;
              			yield $c;
              			$word_generator = $gndn();
              		}
              		switch($c)
              		{
              		case '.':
              			break 2;
              		case ';':
              			$reversing = !$reversing;
              			break;
              		case ' ';
              			break;
              		default:
              			$word_generator = (function()use($word_generator, $c, $reversing)
              			{
              				$reversing and yield $c;
              				yield from $word_generator;
              				$reversing or  yield $c;
              			})();
              		}
              	}
              }
              
              foreach(output_generator() as $c)
              {
              	echo $c;
              }

              Anyhow, I've got to make another pass around the yard to make sure that what should be tied down has been tied down.

                17 days later

                This gives the expected output and should meet your specs.

                
                <?php
                
                function read()
                {
                    static $string = 'This is a test; it is only a test; it is not the real thing; if it were the real thing you would not be reading this; you would be reading something else.';
                    static $position = 0;
                    return isset($string[$position]) ? $string[$position++] : false;
                }
                
                $output_generator = (function() {
                    $reversing = false;
                    $word = '';
                    while(($c = read()) !== false)
                    {
                        if(in_array($c, [' ', ';', '.']))
                        {
                            yield $reversing ? strrev($word) : $word;
                            yield $c;
                            $word = '';
                            if($c == ';')
                            {
                                $reversing = !$reversing;
                            }
                        }
                        else
                        {
                            $word .= $c;
                        }
                    }
                })();
                
                foreach($output_generator as $c)
                {
                    echo $c;
                }

                benanamen
                You don't need to keep reading after a full stop. That character signals the end of the string (indeed, it was the only way the end of the string would be indicated).
                Manipulating a string ($word) is not permitted, since building a string to manipulate is not permitted either.

                  Write a Reply...