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.
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.
ARG generator functions. Just ran across these in Python. Startled to learn that PHP has them, too.
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.