Dzisiaj znowu sobie ponarzekam na PHP, bo znowu odkryłem pewną bardzo dziwną “funkcjonalność” tego języka. Jak wszyscy wiemy udostępnia nam on konstrukcję array(), dzięki której możemy tworzyć dowolne zbiory danych, znane w programowaniu jako tablice, lub, wśród programistów PHP - jako arraye [wym. “araje” ;]]. Okazuje się, że w tak “dowolnej” konstrukcji, niestety nie wszystko jest “dowolne”, a już na pewno nie typy danych, które mogą być używane jako indeksy.

Problem: nie wszystko indeks tablicy, co zawiera zmienna.

Otóż, pewnego pięknego dnia stwierdziłem, że napiszę jakiś ciekawy skrypcik, oczywiście w PHP. Po krótkim zastanowieniu i przeiterowaniu po liście potencjalnych CPNS [Ciekawych Pomysłów Na Skrypty] stwierdziłem, że najciekawszym na daną chwilę pomysłem będzie rysowanie wykresów i wysyłanie do przeglądarki obrazka wynikowego. Techniczne sprawy [tak, użyłem GD] pominę, ponieważ nie są istotne.

Algorytm był trywialnie prosty - jedna funkcja odpowiadała za zwracanie wartości dla podanego argumentu [dzięki temu uniknąłem problemu parsowania wyrażeń matematycznych], a następnie jedna “magia” przeliczała wszystko na tablicę asocjacyjną [argument => wartość funkcji], a druga “rysowała” punkty na obrazku.

Problem pojawił się, kiedy zechciałem z jednego miejsca sterować dokładnością generowanego obrazu. W momencie, kiedy ustawiłem dokładność na 1 [słownie: jeden], w tablicy były umieszczane tylko argumenty całkowite, więc wszystko działało poprawne. Kiedy jednak ustawiłem ją na mniejszą wartość, zauważyłem, że wykres poszerza się poprawnie, ale nadal wyświetla tylko “kropki” elementów całkowitych. Przetestujmy prosty kod:

<?php
echo '<pre>';
$arr = array();
for($i = 0; $i < 10; $i++)
	{
	$arr[$i] = $i;
	}
var_export($arr);

Jego wynik:

array (
  0 => 0,
  1 => 1,
  2 => 2,
  3 => 3,
  4 => 4,
  5 => 5,
  6 => 6,
  7 => 7,
  8 => 8,
  9 => 9,
)

Oczywiście wszystkiemu winne były liczby “niecałkowite”, umieszczone przez pętlę jako wartości argumentów. Kod:

<?php
echo '<pre>';
$arr2 = array();
for($j = 0; $j < 10; $j += 0.1)
	{
	$arr2[$j] = $j;
	}
var_export($arr2);

Zwróci wynik:

array (
  0 => 1,
  1 => 1.9,
  2 => 2.9,
  3 => 3.9,
  4 => 5,
  5 => 6,
  6 => 7,
  7 => 8,
  8 => 9,
  9 => 10,
)

Przy okazji - dlaczego prawie wszystkie indeksy zostały zaokrąglone do góry? [W tym momencie nic mi do głowy nie przychodzi ;]] PHP chyba nigdy nie przestanie mnie zadziwiać.

Po raz kolejny zrozumiałem wtedy, że w programowaniu nie należy niczego zakładać wprzód, a już szczególnie, że coś będzie działać, a najszczególniej, że będzie działać tak, jak sobie tego życzymy. ;]

Wyjaśnienie: kluczami / indeksami tablicy może być tylko integer lub string.

Szybka lektura manuala na temat tablic w PHP szybko sprowadziła mnie na ziemię, bowiem:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. “8” will be interpreted as 8, while “08” will be interpreted as “08”). Floats in key are truncated to integer. The indexed and associative array types are the same type in PHP, which can both contain integer and string indices.
Krótkie podsumowanie wniosków z zacytowanego fragmentu:
  • Kluczem / indeksem tablicy może być tylko integer lub string.
  • Jeśli zechcemy podstawić pod indeks tablicy “string liczbowy”, to zostanie on skonwertowany do liczby.
  • Liczby zmiennoprzecinkowe są “obcinane” do całkowitych.
  • Tablice mogą zawierać zarówno indeksy liczbowe jak i te złożone z łańcuchów znaków (stringów)
W tym momencie po raz kolejny możemy długo i wzniośle westchnąć “ahaaaa”.

Podsumowanie: czy to błąd, czy funkcjonalność?

Szczerze mówiąc, nie widziałem jeszcze, żeby jakikolwiek język oferował możliwość używania liczb zmiennoprzecinkowych jako indeksów tabeli [albo po prostu nie pamiętam, jeśli takie znacie, podzielcie się w komentarzach ;]]. Nie wydaje mi się jednak, żeby wspieranie tego w interpreterach pokroju PHP było jakimś szczególnym problemem. Szczególnie, że wystarczyłoby po prostu zmienić zachowanie i wyłączyć “interpretację” stringów liczbowych - wtedy programiści na pewno by sobie z tym poradzili.