2

I have absolutely no idea, so if someone can point me in the right direction, it'd be greatly appreciated.

I want to have something like

<?php
function square($num) {
// something
}

name('{3}'); // have this return 9
name('{6}'); // have this return 36
name('{{{2}}}'); // have this return 256
name('{9}{12}'); // have this return 81144
name('{{5}}'); // have this return 125
name('adscdc{4}{{3}}'); // have this return adscdc1681
?>

Does anyone have any idea how this can be done? Thanks in advance :) So far, I have:

<?php
function square($text) {
    $parts = explode('{', $text);
    foreach($parts as $part) {
        $piece = explode('}', $part);
        $text = str_replace('{' . $piece[0] . '}', pow($piece[0], 2), $text);
    }
    return $text;
}
echo square('aasd{3}');
?>
3
  • This seems like homework. What do you have so far? Commented Sep 19, 2010 at 21:55
  • 1
    This shouldn't have to be recursive. Was that a requirement? Commented Sep 19, 2010 at 21:56
  • This isn't homework. I have managed to get {3}, but not {{3}}.
    – Tech163
    Commented Sep 19, 2010 at 21:59

6 Answers 6

3

I think {{5}} should be 625, right?

Anyway, try something like this:

<?php

  function name ($str) { 
    while (1) {
      preg_match('/{(\d+)}/', $str, $matches);
      if (count($matches)<2) return $str;
      $str=str_replace($matches[0], $matches[1]*$matches[1], $str);
    }
  }

  echo "\n<pre>\n".
       name('{3}'). // have this return 9
       "\n".
       name('{6}'). // have this return 36
       "\n".
       name('{{{2}}}'). // have this return 256
       "\n".
       name('{9}{12}'). // have this return 81144
       "\n".
       name('{{5}}'). // have this return 625
       "\n".
       name('adscdc{4}{{3}}'). // have this return adscdc1681
       "\n</pre>\n";

?>

...running that gave me these results:

9
36
256
81144
625
adscdc1681
2
  • no problem :) I thought about it and edited it a bit so the function returns values instead of echoing them... it's a little more straightforward now I think. Commented Sep 19, 2010 at 22:54
  • You can also make it a do, while loop, if you don't like while(1) - codepad.viper-7.com/w7Q77N ------ Though in this case, I'm not sure that it's any clearer. Commented Sep 20, 2010 at 0:30
2

You'll probably need to parse the string. Keep reading characters till you reach an open brace, then on each open brace increment a counter. When you get to a number raise it to the power of the counter. Then decrement for each closing brace, and if the braces don't match throw an exception.

7
  • Perhaps using str_split, and then foreach parse through them?
    – Tech163
    Commented Sep 19, 2010 at 22:02
  • @Tech163 Just iterate through the characters of the string.
    – Skilldrick
    Commented Sep 19, 2010 at 22:05
  • 1
    @TokenMacGuy - this is the perfect example of something that's just painful with regex and simple without.
    – Skilldrick
    Commented Sep 19, 2010 at 22:08
  • 1
    @TokenMacGuy: you should have a look at my answer and reconsider. Doing it with regex is nearly a stupid oneliner if you know the right options.
    – kriss
    Commented Sep 19, 2010 at 22:27
  • "when you get to a number" ? That means parsing the string digit by digit and computing the number on the fly, or at least storing all digits of the number in a string. Sure it can be done this way, but why reinvent the wheel when regexes can do nearly everything necessary ? Also where in the question is it said than non matching braces are forbidden ? The question give us no hint on that.
    – kriss
    Commented Sep 19, 2010 at 22:35
1

I thought this problem is interesting. But I already see some good answers in PHP, so just for the porpuse of training myself I implemented a solution in Python. If you are interested, you can have a look here:

>>> import re
>>> test = [('5','5'),('{3}','9'),('{6}','36'),('{{{2}}}','256'),('{9}{12}','81144'),('adscdc{4}{{3}}','adscdc1681')]
>>> def eval(txt):
...   replace = lambda t: re.sub('{(\d+)}',lambda match: str(int(match.group(1)) * int(match.group(1))),t)
...   while(re.search('[{}]',txt) != None):
...     txt = replace(txt)
...   return txt
... 
>>> for t in test:
...   print (t[0],t[1],eval(t[0]) == t[1])
... 
('5', '5', True)
('{3}', '9', True)
('{6}', '36', True)
('{{{2}}}', '256', True)
('{9}{12}', '81144', True)
('adscdc{4}{{3}}', 'adscdc1681', True)

It is also interesting to see, that python while beeing a very strong language for many solutions, often produces pretty ugly code, when handling regexes.

(This post is not intended as answer to the posted problem, because there are already enough good answers. I just share it for the interested eyes.)

1

Looks simple. When you detect a motive like a number inside curly brackets it should be replaced by the square of the number. If no replacement where done you are finished. If some replacement where done you do it again (because what was just replaced may be now inside curly brackets) and so on.

The first part (replacement) can be done using regex, the second part can be done either recursively either or iteratively.

Below some code snippet that should help you understand the details (not the full answer, but not far). The goal of the exercice seems to be to help you understand preg_replace() parameters.

<?php
$count = 0;
echo preg_replace('/[{](\d+)[}]/e', '$1*$1', '{{{2}}}', -1, $count);
echo "\n";
echo "$count replacement done\n";
?>

As others proposed full solutions, here his mine:

<?php

function square($str){
    $count = 0;
    do {
        $str = preg_replace('/[{](\d+)[}]/e', '$1*$1', $str, -1, $count);
    } while ($count);
    return $str;
}

echo square('{3}')."\n"; // have this return 9
echo square('{6}')."\n"; // have this return 36
echo square('{{{2}}}')."\n"; // have this return 256
echo square('{9}{12}')."\n"; // have this return 81144
echo square('{{5}}')."\n"; // have this return 125
echo square('adscdc{4}{{3}}')."\n"; // have this return adscdc1681

?>

If your are preoccupied with compatibility issues (because hardened php installation may forbid use of /e) just use preg_replace_callback instead of preg_replace like below (use anonymous function available as of PHP 5.2.3, for older php version, you can use create_function()).

function square($str){
    $count = 0;
    do {
        $str = preg_replace_callback('/[{](\d+)[}]/',
                    function($m) {return $m[1]*$m[1];},
                    $str, -1, $count);
    } while ($count);
    return $str;
}

For curious readers, as someone else suggested a python version, below is a possible perl equivalent:

#!/usr/bin/perl

my @tests = ('{3}','{6}','{{{2}}}','{9}{12}','{{5}}', 'adscdc{4}{{3}}'); 

sub square {
    my $str = pop;
    while ($str =~ s/[{](\d+)[}]/$1*$1/e) {};
    return $str;
}

for my $str (@tests){
    print "$str --> ".square($str)."\n" ;
} 

There security mechanisms in Perl to avoid injections because of evil user inputs different than blindly rejecting all evals. For those interested you can have a look here.

And two other shorter python versions, recursive:

import re
test = ['5','{3}','{6}','{{{2}}}','{9}{12}','adscdc{4}{{3}}']

def square(txt):
   txt2 = re.sub('{(\d+)}',lambda m: str(int(m.group(1)) ** 2) , txt)
   if txt2 == txt:
        return txt
   return square(txt2)

for x in test:
    print("%s --> %s" % (x, square(x)))

and non recursive

import re
test = ['5','{3}','{6}','{{{2}}}','{9}{12}','adscdc{4}{{3}}']

def square(txt):
    oldtxt = None
    while oldtxt != txt:
        oldtxt = txt
        txt = re.sub('{(\d+)}',lambda m: str(int(m.group(1)) ** 2) , oldtxt)
    return txt

for x in test:
    print("%s --> %s" % (x, square(x)))
8
  • @stereofrog: you are right, the grouping is just a readability habit (something suggested by Damien Conway in 'Perl Best Practices' that caught with me).
    – kriss
    Commented Sep 19, 2010 at 22:44
  • the /e modifier (PREG_REPLACE_EVAL) is the problem here. It's seen as a security risk because it works like eval. Mods like Suhosin disable /e, so it won't work everywhere... Commented Sep 19, 2010 at 23:17
  • @no:yes, but as this question is not tagged hardened-php or Suhosin, I believe your comment is irrelevant. Also this particular use of /e is not a security issue here. At least you should explain in your answer why you are not taking the direct path but some workaround (and not the direct one: use preg_replace_callback instead of /e).
    – kriss
    Commented Sep 20, 2010 at 0:04
  • The /e modifier, like using eval, is something one should normally want to avoid if possible, esp. if the workaround requires like one line of additional code. I don't think any explanation is generally required as to why things like eval are best avoided. Commented Sep 20, 2010 at 0:27
  • @no: looks like something you repeat but can't explain. If it was introduced initially in many languages there is reasons (there is things you can't easily do without eval). But here what's mostly bothering me is doing costly strings searches two times instead of one calling preg_match then preg_replace instead to avoid preg_replace allegedly to avoid calling eval. That also makes code two times as hard to maintain and two two times harder to debug.
    – kriss
    Commented Sep 20, 2010 at 5:50
0

This getting {} brackets and for each bracket get decimal in her and power to number of bracket nested, output was concated to string in place of parsed brackets without any separation character.

0

This necessarily needn't be a recursive function. Apart from the core logic others mentioned, you need to make sure if the input string is valid. For instance examples like:

name('{{9}{{{{{1}');
name('{abbc}');
name('{}');
2
  • The brace matching is handled by the algorithm I outlined - increment on an open, decrement on a close, and check that it gets back to zero before a new increment. You're right about checking things like non-numeric characters within braces though. I think including non-numeric characters in the input is a bit of a stupid requirement anyway.
    – Skilldrick
    Commented Sep 19, 2010 at 22:14
  • 1
    It's handled by the regex solution too. The string is always valid, unmatched braces become literal characters. If you want strict checking, check the resulting string and throw an exception if it contains any { or } characters. Commented Sep 19, 2010 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.