Get your text spinner code here

Status
Not open for further replies.
One liner FTW

PHP:
 preg_replace('/\{([^}]*)}/e',"eval('\$a=explode(\"|\",\"\\1\");shuffle(\$a);return array_pop(\$a);')",$spin);

Awesome. Cunning use of eval.

Although (and I know this thread has gone on long enough...) unless I'm missing something this doesn't handle nested spins.

i.e {text 1|text 2|text 3} but not {text 1|text 2|{text 3|text4}}
 


OK, now I'm definitely missing something. How would you take my example line
Code:
$text = "{{Great|Awesome|Fantastic|Brilliant} {link|site|website} - {highly|thoroughly|totally} recommended|Terrible site, don't visit it}";
And generate 30 (or more) different spins of it, using your code?
 
Using eval is cheating.

I don't know of any circumstance offhand where you'd want to just repeat a "spun" phrase over and over in the same spot. I could see it inside a template of some sort, like WordPress comments, etc, but just spitting out a list doesn't seem very useful.
 
Using eval is cheating.

:P depends on how you're using eval, its a good way to generate some dynamic code on the fly, especially if you're into any kind of obfuscation/encryption of your original source.

In coding, its not cheating if it works... it just might be a shitty-as-fuck-method... but no single function by itself is shitty, only how it's used by the coder.
 
:P depends on how you're using eval, its a good way to generate some dynamic code on the fly, especially if you're into any kind of obfuscation/encryption of your original source.

In coding, its not cheating if it works... it just might be a shitty-as-fuck-method... but no single function by itself is shitty, only how it's used by the coder.
I was just referring to the "one line" statement; yeah eval definitely has its uses. I thought that was hilarious he pretty much replaced all that stuff with a one-liner.

I didn't know about shuffle(), so I learned something from it.
 
I don't know of any circumstance offhand where you'd want to just repeat a "spun" phrase over and over in the same spot.

I use it for social bookmarking to create a variety of titles and descriptions. So you can send those 30 variations from my example to 30 different bookmarking sites.

I thought that was hilarious he pretty much replaced all that stuff with a one-liner
Although I freely admit my code sucks balls and I would love to replace its slow-arse with something quick, as far as I can tell that one-liner only works with something simple like
Code:
$text = "{one|two|three} and some text {four|five} blah blah {six|seven|eight}";
Make it
Code:
$text = "{one|two|three} and some text {four|five} blah blah {six|seven|{eight|nine}}";
and it flounders. The hardest part of the whole thing is getting the nesting to work. The /\{([^}]*)}/e regex won't pick up any nested spins, even in a while loop.
 
I think your code is good. I only did this because I thought the challenge would help keep me practiced. I'm not sure and haven't tested it too thoroughly but it seems to do what it's supposed to.
One problem with both of scripts is that they do more work than they need to. Mine seems to run the spinText function about 3 times more(using this test string) than it needs to to get 30 unique strings.
There's probably some sort of technique or algorithm you can use as a base to approach the problem differently to cut out this inefficiency. I don't know maybe the code just sucks.


Code:
<?php

$text = "{{Great|Awesome|Fantastic|{Brilliant|{Nifty|Spiffy|{clever|cool}}}} {link|site|website|{titty bar|jail}} - {highly|thoroughly|totally} recommended|Terrible site, don't visit it}";

print_r(spin($text, 30));

function spin($text, $numToSpin=10) {

  if (substr_count($text, '{') != substr_count($text, '}'))
  { // surely we're all using PHP5 by now.
    throw new Exception("Number of opening and closing curly braces don't match.");
  }

  $i = 0;
  $spun = array();

  // arbitrary cut off at 1000, should be done better
  while (count($spun) < $numToSpin && $i < 1000)
  { 
    $spunText = spinText($text);

    // crc32 is supposed to be faster than most. Hopefully it's collision proof enough.
    // I'm not sure hashing is the best solution.
    // Instead of hashing could store the keys and a boolean value then return the keys 
    // as an array. 
    $keyHash = sprintf("%u", crc32($spunText));

    if (array_key_exists($keyHash, $spun))
      continue;

    $spun[$keyHash] = $spunText;

    $i++;
  }

  return array_values($spun);
}

function spinText($text)
{ 
  $i = 0; 
  // another arbitrary cutoff point
  while ($i < 1000)
  {
    // There might be a better way besides assigning to temp variable then reassigning back. 
    // If there's a lot of text this could be costly.
    // I start replacing from the inside out. The inner most nested get replaced first. 
    // When the text stops changing I call it quits.
    $tmpText = preg_replace_callback('#{([^{}]+)}#s', pregcallback, $text);

    if (strcmp($tmpText, $text) == 0)
      break;

    $text = $tmpText;

    $i++;
  }

  return $text;
}

function pregcallback($matches) {

  $choices = explode('|', preg_replace('#(^{|}$)#', '', $matches[1]));

  return $choices[array_rand($choices)];
}


?>
 
Ugh, can't edit post above to include this. Anyway, just running a few tests from the command line with the same string, it looks like my version could be more than 20 times faster.
 
$i needs to be incremented at the top of the loop. Just tested it on bigger text and it choked if the number of spins was set higher than the number of possible combinations. Because the counter wasn't being incremented.


Code:
  // arbitrary cut off at 1000, should be done better
  while (count($spun) < $numToSpin && $i < 1000)
  { 
    $i++;
    
    $spunText = spinText($text);
 
LogicFlux, your code is much quicker than mine - a script that was taking anything up to 0.5 of a second to run now runs in 0.02, so it's a major saving.

You're right there are still improvements to be made, with the arbitrary cutoffs at 1000 and the 'choice' selection with array_rand, but for most applications this is fast enough I think.

Nice one.
 
Status
Not open for further replies.