Dlaczego ten kod po prostu nie drukuje liter od A do Z?

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

Ten fragment kodu daje następujące wyniki (znaki nowej linii są zastępowane spacjami):

abcdefghijklmnopqrstu vwxyz aa ab ac ad ae af ag ah ai aj ak al am ao ap aq ar as at au av aw ax ay az ba bb bc bd be bf bg bh bb bi bj bk bl bm bn bo bp bq br bs bt bb bv bw bx przez 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 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 ex ... on to yz

Milan Babuškov
źródło
31
PHP nie jest C, nawet jeśli składnia próbuje cię przekonać, że jest inaczej.
joni
3
Działa to dla mnie z bardzo małą zmianą: for ($ i = 'a'; $ i! = 'Aa'; $ i ++) {echo "$ i \ n"; }
Surreal Dreams
2
Komentarz na temat tego, że PHP nie jest C - był najostrzejszy, przykład: in c: char c = 'a'; nie jest taki sam jak w php: $c = 'a';chodzi o to, że w C występuje typ char (symbol znaku 1), ale nie w PHP, jeśli powiesz PHP $c = 'a';- oznacza to, że jest to ciąg zawierający tylko 1 znak. Dlatego U nie może poprawnie przechodzić przez 28 znaków w PHP. Mam nadzieję, że każdy programista nauczy się języków niskiego poziomu i pisania na klawiaturze, nie zapominając o praktykach matematycznych, które pomogą im być silniejszym.
Arthur Kushman
Wow, to jest naprawdę fajne, ale dlaczego nie zatrzymało się na „z”
Prasanth Bendra
Aby uzyskać oczekiwany punkt końcowy za pomocą równości ( ==lub !=), sprawdź tę odpowiedź na powiązane pytanie .
IMSoP,

Odpowiedzi:

342

Z dokumentów :

PHP postępuje zgodnie z konwencją Perla, jeśli chodzi o operacje arytmetyczne na zmiennych znakowych, a nie na C.

Na przykład w Perlu 'Z'+1zmienia się w 'AA', podczas gdy w C 'Z'+1zmienia się w '['( ord('Z') == 90, ord('[') == 91).

Należy pamiętać, że zmienne znakowe można zwiększać, ale nie zmniejszać, a mimo to obsługiwane są tylko zwykłe znaki ASCII (az i AZ).

Z komentarzy: -
Należy również zauważyć, że<=jest to porównanie leksykograficzne, więc'z'+1 ≤ 'z'. (Od'z'+1 = 'aa' ≤ 'z'. Ale'za' ≤ 'z'porównanie jest po raz pierwszy fałszywe.) Na przykład przerwanie, kiedy$i == 'z'zadziałałoby.

Przykład tutaj .

CMS
źródło
Hah .. to szalone! Zawsze używałem, ord()więc nigdy tego nie zauważyłem.
mpen
68
Dla kompletności należy również dodać, że „<=” to porównanie leksykograficzne, więc „z” + 1 ≤ „z”. (Ponieważ „z” + 1 = „aa'≤'z”. Ale „zz'≤'z” to pierwsze porównanie, które jest fałszywe.) Na przykład łamanie, gdy $ i == 'z' działałoby.
ShreevatsaR
6
jak mówi ShreevatsaR, to komparator, a nie arytmetyka, to jest problem, nie skupiaj się na operatorze ++
slf
10
@ShreevatsaR: faktycznie „yz” + 1 = „za”. Pierwsze niepowodzenie porównania to „za” <= „z”
Milan Babuškov,
2
Dzięki za komentarze! Tak, kluczową kwestią jest to, że 'aa'jest leksykograficznie mniejszy 'z', dlatego pętla trwa. I zatrzymuje się, 'yz'ponieważ 'za'jest większy niż z. Sprawdź ten przykład .
CMS
123

Ponieważ po osiągnięciu „z” (i jest to prawidłowy wynik w twoim zakresie, $ i ++ zwiększa go do następnej wartości w sekwencji), następną wartością będzie „aa”; a alfabetycznie „aa” to <„z”, więc porównanie nigdy nie jest spełnione

for ($i = 'a'; $i != 'aa'; $i++) 
    echo "$i\n"; 
Mark Baker
źródło
55
Dziwne jest to, że „z” ++ = „aa”, ale „aa” <„z”. Ta logika nie płynie zbyt dobrze.
Matthew Vines,
19
@Matthew: Alfabetycznie. „aa” byłby pierwszy, a więc „mniej niż” ciąg „z”. Pętla kończy się na „zz”, ponieważ jest alfabetycznie „większa niż” (występuje po) „z”. Jest to nielogiczne w tym sensie, że można „zwiększyć” coś i uzyskać mniejszą wartość, ale jest logiczne w sensie alfabetycznym.
eldarerathis
2
Inkrementatorem znaków jest logika Perla (patrz cytat CMS z dokumentacji). Porównanie „aa” <„z” jest standardową logiką porównywania ciągów. Nic dziwnego, kiedy zrozumiesz, jak go używać ... z odpowiedzi tutaj wiele osób nie rozumie.
Mark Baker
5
@eldarerathis Oh Zdecydowanie rozumiem, jak to działa. Po prostu wydaje mi się to dziwne w tym samym czasie.
Matthew Vines,
2
Jest to dla mnie niezwykle przydatne, grając z kolumnami Excela, które mają tę samą logiczną serię
Mark Baker
97

Inne odpowiedzi wyjaśniają obserwowane zachowanie opublikowanego kodu. Oto jeden ze sposobów robienia tego, co chcesz (i to czystszy kod, IMO):

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

W odpowiedzi na komentarz / pytanie ShreevatsaR dotyczące funkcji zakresu : Tak, tworzy „właściwy punkt końcowy”, tzn. Wartości przekazywane do funkcji znajdują się w zakresie. Aby to zilustrować, wynik z powyższego kodu był następujący:

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
źródło
2
Czy range () obejmuje odpowiedni punkt końcowy? Z doświadczenia z innymi językami jest to również nieoczekiwane!
ShreevatsaR
1
@ShreevatsaR: Tak, range () daje „właściwy” punkt końcowy, zobacz moją zredagowaną odpowiedź (i kliknij link do funkcji), aby uzyskać więcej informacji.
GreenMatt,
1
Co mogę powiedzieć… więcej szaleństwa PHP. :-) Nie znam innego języka, w którym zakres () działałby w ten sposób. (Na pewno nie, powiedzmy, Haskell czy Python.) Czy Dijkstra nie napisał o tym czegoś?
ShreevatsaR
10
Szaleństwo jest niespójne z PHP. zakres („A”, „CZ”) działa zupełnie inaczej niż inkrementator ++, a wynikowa tablica będzie zawierać tylko trzy wartości: A, B i C.
Mark Baker
35

Inni już powiedzieli, dlaczego PHP nie pokazuje tego, czego oczekujesz. Oto, w jaki sposób możesz uzyskać pożądany wynik:

<?php
for ($i = ord('a'); $i <= ord('z'); $i++)
    echo chr($i);
?>
Filip Ekberg
źródło
2
Niepotrzebne. wcale nie musisz wykonywać ord (), wystarczy prawidłowe porównanie, aby zakończyć pętlę
Mark Baker
2
+1 Znacznie bardziej zrozumiałe, gdy nie jest zaznajomiony z jedną z bardziej ekscentrycznych funkcji PHP.
lonesomeday
1
Jest to doskonały przykład kodu samodokumentującego. Jest łatwo zrozumiały dokładnie, ponieważ używa wartości porządkowych, a następnie wyświetla zmienną jako znak. Pętla for byłaby bardziej wydajna, gdyby test wartości maksymalnej był określony tylko raz w następujący sposób: „for ($ i = ord ('a'), $ max = ord ('z'); $ i <= $ max; $ i ++) {"
slevy1
22

Dlaczego nie po prostu użyć range('a','z')?

bcosca
źródło
4

Wypróbuj ten kod. Myślę, że ten kod będzie ci pomocny.

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

Wyświetlaj 26 liter po kolei.

Chinmay235
źródło
2
<?php

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

?>
Pan Griever
źródło
2

Można to również wykorzystać:

for ($i = 'a'; $i <= 'z'; $i=chr(ord($i)+1))
    echo "$i\n";
LRA
źródło
2

PHP ma funkcję zapętlania liter i może przekraczać pojedyncze znaki; reszta zostanie wykonana w ten sposób: aa ab ac ... zz i tak dalej.

Spróbuj tego:

<?php
for ($i = 'a'; $i !== 'aa'; $i++)
    echo "$i\n";
?>
James Dantes
źródło
0

Chociaż powyższe odpowiedzi są wnikliwe w to, co się dzieje, i dość interesujące (nie wiedziałem, że tak się zachowa i dobrze jest zrozumieć, dlaczego.

Najłatwiejszym rozwiązaniem (choć może nie najbardziej znaczącym) jest zmiana warunku na $ i! = 'Z'

<?php
for ($i = 'a'; $i != 'z'; $i++)  
    echo "$i\n";
?>
jon_darkstar
źródło
4
Zauważ, że to da ci tylko y, a nie z
Mark Baker
doh! tak, dobra uwaga. Widzę logikę zarówno przyrostu, jak i porównania, ale z pewnością dziwne jest, że czasami $ a ++ <$ a
jon_darkstar
0

PHP nie uważa, że ​​„AA” jest mniejsze niż „Z”. Najlepszym sposobem na to jest:

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

ABCDEFGHIJKLMNOPQRSTU VWXYZ

Renato Cassino
źródło
0

Być może ten kod będzie działał. To proste i zrozumiałe:

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

gdzie 26 to całkowita liczba liter w alfabecie.

Wyjątek
źródło
-3

Wow, naprawdę nie wiedziałem o tym, ale to nie jest duży kod, możesz wypróbować echo „z” po pętli. Mark ma absolutną rację. Używam jego metody, ale jeśli chcesz alternatywy, możesz również spróbować

<?php
for ($i = "a"; $i = "y"; $i++) {
    echo "$i\n";
    if ($i == "z") {}
}
echo "z";
?>
Mohit Bumb
źródło