¿Por qué este código simplemente no imprime las letras de la A a la Z?

435
<?php
for ($i = 'a'; $i <= 'z'; $i++)
    echo "$i\n";

Este fragmento proporciona el siguiente resultado (las nuevas líneas se reemplazan por espacios):

abcdefghijklmnopqrstu vwxyz aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba bb bc bd be bf bg bh bi bj bk bl bm bn bo bp bq br bs bt bu bv bw bx por bz ca cb cc cd ce cf cg ch ci cj ck cl cm cn co cp cq cr cs ct cu cv cw cx cy cz da db dc dd de df dg dh di dj dk dl dm dn do dp dq dr ds dt du dv dw dx dy dz ea eb ec ed ee ef eg eh ei ej ek el em en eo ep eq er es et eu ev ew ex ... on to yz

Milan Babuškov
fuente
31
PHP no es C, incluso si la sintaxis intenta convencerte de lo contrario.
joni
3
Esto funciona para mí con un cambio muy pequeño: for ($ i = 'a'; $ i! = 'Aa'; $ i ++) {echo "$ i \ n"; }
Surreal Dreams el
2
El comentario sobre que PHP no es C - fue el más agudo, ejemplo: en c: char c = 'a'; no es lo mismo que en php: $c = 'a';el punto es que en C hay un tipo de char (símbolo de carácter 1), pero no en PHP, si U le dice a PHP $c = 'a';, significa que es una cadena con solo 1 carácter. Es por eso que U no puede recorrer 28 caracteres adecuadamente en PHP. Espero que todos los programadores aprendan idiomas de bajo nivel y tipeo fuerte junto con ellos, sin olvidar las prácticas matemáticas, que los ayudarán a ser más fuertes.
Arthur Kushman
Wow, eso es realmente genial, pero por qué no se detuvo en "z"
Prasanth Bendra
Para obtener una forma de obtener el punto final esperado utilizando la igualdad ( ==o !=), consulte esta respuesta a una pregunta relacionada .
IMSoP

Respuestas:

342

De los documentos :

PHP sigue la convención de Perl cuando trata con operaciones aritméticas en variables de caracteres y no en C.

Por ejemplo, en Perl se 'Z'+1convierte en 'AA', mientras que en C se 'Z'+1convierte en '['( ord('Z') == 90, ord('[') == 91).

Tenga en cuenta que las variables de caracteres pueden incrementarse pero no disminuirse y, aun así, solo se admiten caracteres ASCII simples (az y AZ).

De los comentarios: -
También debe notarse que<=es una comparación lexicográfica, entonces'z'+1 ≤ 'z'. (Desde entonces'z'+1 = 'aa' ≤ 'z'. Pero'za' ≤ 'z'es la primera vez que la comparación es falsa). Romper cuándo$i == 'z'funcionaría, por ejemplo.

Ejemplo aquí .

CMS
fuente
Ja ... eso es una locura! Siempre he usado, ord()así que nunca me di cuenta de esto.
mpen
68
Para completar, también debe agregar que "<=" es una comparación lexicográfica, entonces 'z' + 1 ≤ 'z'. (Dado que 'z' + 1 = 'aa'≤'z'. Pero 'zz'≤'z' es la primera vez que la comparación es falsa.) Romper cuando $ i == 'z' funcionaría, por ejemplo.
ShreevatsaR
66
como dice ShreevatsaR, el problema es el comparador, no la aritmética, no se centre en el operador ++
slf
10
@ShreevatsaR: en realidad, 'yz' + 1 = 'za'. La primera comparación que falla es 'za' <= 'z'
Milan Babuškov
2
¡Gracias por los comentarios chicos! Sí, el punto clave es que 'aa'es lexicográficamente más pequeño que 'z', por eso el ciclo continúa. Y se detiene 'yz'porque 'za'es mayor que z. Mira este ejemplo .
CMS el
123

Como una vez que se alcanza 'z' (y este es un resultado válido dentro de su rango, $ i ++ lo incrementa al siguiente valor en secuencia), el siguiente valor será 'aa'; y alfabéticamente, 'aa' es <'z', por lo que la comparación nunca se cumple

for ($i = 'a'; $i != 'aa'; $i++) 
    echo "$i\n"; 
Mark Baker
fuente
55
Es extraño que 'z' ++ = 'aa', pero 'aa' <'z'. Esa lógica no fluye muy bien.
Matthew Vines el
19
@Matthew: alfabetízalos. 'aa' vendría primero, por lo tanto, es "menor que" la cadena 'z'. El ciclo termina en 'zz' porque es alfabéticamente "mayor que" (viene después) 'z'. Es ilógico en el sentido de que puede "incrementar" algo y obtener un valor menor, pero es lógico en un sentido alfabético.
eldarerathis
2
El incrementador de caracteres es la lógica de Perl (consulte la cita de CMS de los documentos). La comparación 'aa' <'z' es una lógica de comparación de cadenas estándar. No es extraño, una vez que entiendes cómo usarlo ... de las respuestas aquí, mucha gente no.
Mark Baker, el
55
@eldarerathis Oh, definitivamente entiendo cómo está funcionando. Simplemente me parece extraño al mismo tiempo.
Matthew Vines el
2
Es increíblemente útil para mí, jugar con columnas de Excel que siguen la misma serie lógica
Mark Baker, el
97

Otras respuestas explican el comportamiento observado del código publicado. Aquí hay una forma de hacer lo que quiere (y es un código más limpio, IMO):

foreach (range('a', 'z') as $i)
    echo "$i\n";

En respuesta al comentario / pregunta de ShreevatsaR sobre la función de rango : Sí, produce el "punto final correcto", es decir, los valores pasados ​​a la función están en el rango. Para ilustrar, el resultado del código anterior fue:

a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
GreenMatt
fuente
2
¿Range () incluye el punto final correcto? Por experiencia con otros idiomas, ¡eso también es inesperado!
ShreevatsaR
1
@ShreevatsaR: Sí, range () proporciona el punto final "correcto", vea mi respuesta editada (y siga el enlace a la función) para obtener más información.
GreenMatt
1
Qué puedo decir ... más locura de PHP. :-) No conozco otro idioma en el que range () funcione de esta manera. (Ciertamente no, digamos, Haskell o Python.) ¿Dijkstra no escribió algo sobre esto?
ShreevatsaR
10
La locura está en la inconsistencia de PHP. range ('A', 'CZ') funciona de manera totalmente diferente al incrementor ++, y la matriz resultante solo contendrá tres valores: A, B y C.
Mark Baker,
35

Otros ya dijeron por qué PHP no muestra lo que esperas. Así es como obtienes el resultado que deseas:

<?php
for ($i = ord('a'); $i <= ord('z'); $i++)
    echo chr($i);
?>
Filip Ekberg
fuente
2
Innecesario. no necesita hacer ord (), solo la comparación correcta para terminar el ciclo
Mark Baker,
2
+1 Mucho más comprensible cuando no está familiarizado con una de las características más excéntricas de PHP.
solitario el
1
Este es un excelente ejemplo de código autodocumentado. Es fácilmente comprensible precisamente porque usa valores ordinales y luego muestra la variable como un carácter. El ciclo for sería más eficiente si la prueba para el valor máximo se determina solo una vez de la siguiente manera: "for ($ i = ord ('a'), $ max = ord ('z'); $ i <= $ max; $ i ++) {"
slevy1
22

¿Por qué no solo usar range('a','z')?

bcosca
fuente
4

Prueba este código. Creo que este código te será útil.

$alphas = range('A', 'Z');
foreach($alphas as $value){
    echo $value."<br>";
}

Muestra 26 letras en secuencia.

Chinmay235
fuente
2
<?php

$i = 'a';
do {
echo ($j=$i++),"\r\n";
} while (ord($j) < ord($i));

?>
Sr. Griever
fuente
2

También esto se puede usar:

for ($i = 'a'; $i <= 'z'; $i=chr(ord($i)+1))
    echo "$i\n";
LRA
fuente
2

PHP tiene la función de bucle de letras y puede exceder más allá de los caracteres individuales; el resto se hará de esta manera: aa ab ac ... zz, y así sucesivamente.

Prueba esto:

<?php
for ($i = 'a'; $i !== 'aa'; $i++)
    echo "$i\n";
?>
James Dantes
fuente
0

Si bien las respuestas anteriores son reveladoras de lo que está sucediendo y son bastante interesantes (no sabía que se comportaría así, y es bueno ver por qué).

La solución más fácil (aunque quizás no sea la más significativa) sería simplemente cambiar la condición a $ i! = 'Z'

<?php
for ($i = 'a'; $i != 'z'; $i++)  
    echo "$i\n";
?>
jon_darkstar
fuente
44
Tenga en cuenta que esto solo le dará un
Mark Baker, el
doh! Sí, buen punto. Puedo ver la lógica detrás del incremento y la comparación, pero es extraño que a veces $ a ++ <$ a
jon_darkstar el
0

El PHP no considera 'AA' menos que 'Z'. La mejor manera de hacer esto es:

for($i = 'a'; $i != 'aa'; $i++) {
  echo $i;
}

ABCDEFGHIJKLMNOPQRSTU VWXYZ

Renato Cassino
fuente
0

Quizás este código funcione. Es fácil y se puede entender:

<?php
$ascii_val = ord("a");
for($i=$ascii_val;$i<$ascii_val+26;$i++){
echo chr($i)."\n";
}
?>

donde 26 es el número total de letras en el alfabeto.

Excepción
fuente
-3

Wow, realmente no sabía sobre esto, pero no es un código grande, puedes probar echo "z" después del bucle Mark es absolutamente correcto. Uso su método, pero si quieres una alternativa, entonces también puedes probar

<?php
for ($i = "a"; $i = "y"; $i++) {
    echo "$i\n";
    if ($i == "z") {}
}
echo "z";
?>
Mohit Bumb
fuente