4
\$\begingroup\$

I got this task to evaluate my knowledges in PHP. I was asked to avoid using functions like strrev() or array_reverse().

Apparently they did not like my solution. Can you provide details as to what is wrong with it or how it could be done better?

function reverse_string($str)
{
    if(!strlen($str)){
        return '';
    }
    $reversed = '';
    for($i=strlen($str)-1;$i>=0;$i--){
        $reversed .= $str[$i];
    }
    return $reversed;
}
echo reverse_string('abcdefg3.14');

Output

41.3gfedcba
\$\endgroup\$
16
  • 4
    \$\begingroup\$ "Apparently they did not like my solution" - did they provide any specific feedback? \$\endgroup\$
    – Chris Haas
    Commented Jul 9 at 15:39
  • 1
    \$\begingroup\$ Anything not working with the given code? How should we know what anybody else does not like about your solution? \$\endgroup\$
    – Nico Haase
    Commented Jul 9 at 15:47
  • 8
    \$\begingroup\$ There's a couple of things I can think of. First, I asked AI to do this and it spit out pretty much what you wrote. I'm not saying you used AI, but maybe they thought that. Second, this actually technically reversed the bytes of a string. For ASCII that is the same but for multi-byte characters this breaks things. Third, maybe they want comments? Fourth, maybe they expect type declarations on your function. Fifth, maybe they don't like your variables names. These are just guesses, obviously. If you don't hear anything back, be glad, it doesn't sound like it would be a great place to work. \$\endgroup\$
    – Chris Haas
    Commented Jul 9 at 15:47
  • 4
    \$\begingroup\$ I'm curious how you got from "they selected another candidate" to "it's because they did not like my solution to this one particular question." Surely they were considering other factors. \$\endgroup\$ Commented Jul 9 at 15:58
  • 3
    \$\begingroup\$ Yep, exactly. Surely the interview consisted of more than just this one simple question. They didn't tell you anything was wrong with your answer but you came to the conclusion that it could be the only possible reason. It sounds very presumptive to me. I interview lots of developers. There are miniscule nitpicks here but there is nothing that warrants dismissal of a candidate. You were rejected for something else. \$\endgroup\$ Commented Jul 9 at 16:19

4 Answers 4

3
\$\begingroup\$

That is quite a "basic" example, and yet there are different approaches possible.

PSR

There is one things that puts me off a bit: spacing. Spacing makes code more readable, thus easier to understand. Your coding style is too compact.

As already said PHP, like other languages has some widely accepted coding standards like PSR. Observance of which (or the lack thereof) is a telltale sign of how experienced the programmer is.

Coding is often teamwork, which means your colleagues must be able to maintain other colleagues' code (especially after they have left the company...) and adherence to standards facilitates maintenance.

I don´t know about the conditions in which the exercise took place, but a good IDE with linter could reformat the code for you. If you are only allowed to use notepad, then you should still try to write code more or less compliant. At least it would be apparent you are used to those coding standards, and you are just outside of your comfort zone, which they would understand.

Multibyte

Not handling multibyte string could be problematic too, as the code will not always perform as expected.

Concatenation

A minor detail maybe, but as far as I know PHP doesn't have the equivalent of a Stringbuilder, so concatenation is implemented somewhat rudimentary. On longer strings, this could result in poor performance and memory usage. But you don't even need to concatenate. PHP has a yield statement like in Python, so you could simply consume the string character by character, in a generator fashion. Just a thought.

Edge cases

There is one test in your code, but unfortunately it is not useful.

What if I pass a NULL variable to your function? I guess it would return an empty string but this is flawed.

And if a number or a float is passed to the function you could decide to cast it to a string, or reject it. Implementation is up to you, but you can make this function more robust either way.

While it may not be possible to think of every scenario within the allocated time, it is still a good idea to add some comments and TODO list, just to show that you have thought about the possible pitfalls, and you are aware of what is missing to have production-ready code.

\$\endgroup\$
1
  • \$\begingroup\$ (1.) nit: "pass a NULL variable" Meh. If caller messes up they get what they deserve, a fatal exception that will soon encourage them to fix their bug. A list or a set could be passed in also. But the spec pretty clearly asks for a string as input. (2.) more substantive: "concatenation ... could result in poor performance" What? I disagree. Now, in java and in python 1.5 one can obtain terrible \$n^2\$ quadratic performance, hence StringBuilder. But modern python and modern php demonstrably don't behave that way. We see linear (or anyway \$O(n \log n)\$) behavior. I measured it just now. \$\endgroup\$
    – J_H
    Commented Jul 10 at 23:18
3
\$\begingroup\$

correctness

It's unclear what the specification

reverse a string

really means in this context. To reverse US ASCII like "hello world" is straightforward, and properly handled by the OP code. But the internet is global, and an input like "«déjà vu»" would fare poorly. Is it "a string"? Or would a sensible specification keep calling out that this or that field is "a multibyte string"?

For a text processing solution that can handle "internet text", prefer to call mb_str_split().

special case

    if (!strlen($str)) {
        return '';
    }

It's hard to see why that's even part of the solution. Surely running the for loop through zero iterations should suffice, producing the same result.

linear complexity

You used the .= catenation operator -- good! It results in \$O(n)\$ linear performance. In order to use it, it was necessary to traverse the source string in reverse order.

Definitely do not traverse characters in forward order to build a result using this:

    $reversed = "{$character}{$reversed}";

It is observed to suffer from \$O(n^2)\$ quadratic performance, taking several seconds for a task that should complete within 20 msec. The trouble is we keep copying "the string so far" into a new result string. In contrast .= requests a "big enough" buffer and then keeps extending the result string, without re-reading and re-re-reading partial results. And even with reallocation copying, amortized cost using a constant growth factor will still remain linear. time to reverse a string

\$\endgroup\$
2
  • \$\begingroup\$ I'm a little confused about the last paragraph and performance graph. The performance graph looks exponential to me. It just doesn't look like you've hit the sharp rise upwards. Unless the graph isn't depicting .= performance? \$\endgroup\$ Commented Jul 12 at 0:43
  • 1
    \$\begingroup\$ @GregBurghardt, the performance graph depicts quadratic performance, taking several seconds for a task that should complete within 20 msec. I just took verbatim the loop that appeared in another answer (since changed), with that innocuous looking "{$character}{$reversed}" expression being the core difficulty. Producing a reversed string of length 400 K, one character at a time, took ummm rather longer than expected. Nope, it's not exponential. It's the classic Shlemiel the painter’s algorithm, cf java's StringBuilder. \$\endgroup\$
    – J_H
    Commented Jul 12 at 0:49
2
\$\begingroup\$

Can you provide details as to what is wrong with it or how it could be done better?

Starting the counter length doesn't consider multi-byte characters

As was mentioned in a comment, the first expression of the for loop contains a call to strlen($str) to calculate the first value of $i. Per the documentation of strlen():

Note:

strlen() returns the number of bytes rather than the number of characters in a string.

For multi-byte strings this will not work well as some characters may be broken up. One could use mb_strlen() to get the length of a multi-byte string and mb_substr() to get multi-byte characters at given indexes.

Use idiomatic spacing

While this is purely subjective, most idiomatic PHP follows standards like PSR-12, which has many recommendations on styling and other code conventions. The line which stands out as the most incongruent with such conventions is the loop declaration:

for($i=strlen($str)-1;$i>=0;$i--){

Idiomatic PHP code would have more spaces after the keyword for, the semi-colons and the closing parenthesis, as well as surrounding binary operators:

for ($i = strlen($str)-1; $i>=0; $i--) {

The php.net documentation often has idiomatic code. For example, the documentation for the for keyword contains this example (one of four):

<?php
/* example 1 */

for ($i = 1; $i <= 10; $i++) {
    echo $i;
}
...
\$\endgroup\$
0
2
\$\begingroup\$

function reverse_string($str)

could be

function reverse_string(string $str) : string

Then it would enforce that the input has to be a string immediately rather than failing when strlen is called.

This is an example of something that might fail you on a culture fit basis. Some organizations prefer strong typing even if they use a language like PHP that is traditionally weakly typed.

It's also worth noting that some of these interviews expect you to ask questions to clarify the problem. Not asking questions can fail the interview even if the code complies with the basic description. I.e. the goal of the interview may be to see how you handle ambiguous requirements.

\$\endgroup\$

Your Answer

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