Подтвердить что ты не робот

Лучшая практика: как структурировать массивы - стандарты и соглашения об именах

Какова наилучшая практика в многомерной структуре массива с точки зрения того, какие элементы содержат итератор и элементы детали?

Большая часть моего опыта программирования (и я делаю это в основном для удовольствия) исходит из следующих руководств по Google, поэтому я заранее извиняюсь, если это кажется исключительно глупым вопросом, но я хочу начать улучшать свой код.

Всякий раз, когда мне нужно создавать многомерный массив, мое имя всегда помещало счетчик в первый элемент.

Например, если у меня есть одномерный массив следующим образом:

$myArray['year']=2012;
$myArray['month']='July';
$myArray['measure']=3;
// and so on.

Однако, если бы я хотел, чтобы тот же массив содержал несколько владельцев истории, я бы добавил другое измерение и отформатировал его следующим образом:

$myArray[$owner]['year']=2012;
$myArray[$owner]['month']='July';
$myArray[$owner]['measure']=3;

Изменить: чтобы убедиться, что мой пример не отложен или не ведет в правильном направлении, я в основном следую этой структуре:

$myArray[rowOfData][columnOfData]

Теперь, мой вопрос о принятой конвенции. Должен ли я делать следующее?

$myArray['year'][$owner]=2012;
$myArray['month'][$owner]='July';
$myArray['measure'][$owner]=3;

Изменить: используя это редактирование сверху, должно ли оно быть:

$myArray[columnOfData][rowOfData]

Я искал соглашения об именах массивов, но продолжаю приводить статьи о том, следует ли указывать массивы как множественные числа или нет. То, как я назвал их, кажется более логичным, и я думаю, что это следует за структурой, которая лучше напоминает объект, т.е. object->secondaryLevel->detail, но для всех, кого я знаю, я все время занимаюсь этим. Поскольку я все больше и больше занимаюсь программированием, я бы предпочел изменить свои привычки, если они ошибаются.

Есть ли принятый стандарт или все идет с массивами? Если бы вы искали код, написанный кем-то другим, какой формат ожидал бы? Я понимаю, что любая структура, которая имеет смысл/интуитивно понятна, принимается.

Также с точки зрения итерации, какая из следующих функций более интуитивная?:

for($i=0;$i<$someNumber;$i++)
{
    echo $myArray[$i]['year'];
    // OR
    echo $myArray['year'][$owner];
}

Изменить: у меня был этот пост с тегами как С# и Java, потому что я хотел получить некоторые мнения за пределами PHP-программистов. Я думаю, что, поскольку массивы используются на многих разных языках, было бы полезно получить некоторый вклад от программистов в разных языках.

4b9b3361

Ответ 1

Ваш вопрос субъективен, поскольку каждый может иметь другой подход к ситуации, о которой вы заявили, и вы разумно даже задать вопрос; Как лучше назвать ваши переменные, классы и т.д. Грустно сказать, что я трачу больше времени, чем я хочу признать определение лучших имен переменных, которые имеют смысл и удовлетворяют требованиям. Моя конечная цель - написать код, который является самостоятельным документированием. Написав самодокументирующий код, вы обнаружите, что гораздо проще добавлять функции или исправлять недостатки по мере их возникновения.

На протяжении многих лет я пришел к выводу, что эти практики работают лучше всего для меня:

Массивы: всегда множественное число

Я делаю это, поэтому структуры управления контуром более понятны и легче работать.

// With a plural array it easy to access a single element
foreach ($students as $student) {}

// Makes more sense semantically
do {} (while (count($students) > 0);

Массивы объектов > глубокие многомерные массивы

В вашем примере ваши массивы начали взрываться, чтобы быть 3-х элементными многомерными массивами, и так же корректно, как фрагмент кода Робби, он демонстрирует сложность, которую он выполняет для итерации по многомерным массивам. Вместо этого я бы предложил создать объекты, которые можно добавить в массив. Обратите внимание, что следующий код только демонстративный, я всегда использую аксессоры.

class Owner
{
    public $year;
    public $measure;
    public $month;
}

// Demonstrative hydration 
for ($i = 1 ; $i <= 3 ; $i++) {

    $owner = new Owner();

    $owner->year = 2012;
    $owner->measure = $i;
    $owner->month = rand(1,12);

    $owners[] = $owner;
}

Теперь вам нужно только выполнить итерацию по плоскому массиву, чтобы получить доступ к необходимым вам данным:

foreach ($owners as $owner) {
    var_dump(sprintf('%d.%d: %d', $owner->month, $owner->year, $owner->measure));
}

Прохладная вещь об этом подходе к массиву объектов - это то, насколько легко будет добавлять улучшения, что, если вы хотите добавить имя владельца? Нет проблем, просто добавьте переменную-член в свой класс и немного измените гидратацию:

class Owner
{
    public $year;
    public $measure;
    public $month;
    public $name;
}

$names = array('Lars', 'James', 'Kirk', 'Robert');

// Demonstrative hydration 
for ($i = 1 ; $i <= 3 ; $i++) {

    $owner = new Owner();

    $owner->year = 2012;
    $owner->measure = $i;
    $owner->month = rand(1,12);
    $owner->name = array_rand($names);

    $owners[] = $owner;
}

foreach ($owners as $owner) {
    var_dump(sprintf('%s: %d.%d: %d', $owner->name, $owner->month, $owner->year, $owner->measure));
}

Вы должны помнить, что приведенные выше фрагменты кода - это просто предложения, если вы предпочитаете использовать многомерные многомерные массивы, тогда вам нужно будет выяснить порядок упорядочения элементов, который имеет смысл YOU и с теми, с кем вы работаете, если вы считаете, что у вас возникнут проблемы с настройкой через шесть месяцев после этого, тогда лучше всего реализовать лучшую стратегию, пока у вас есть шанс.

Ответ 2

Вы хотите облегчить себе (и, возможно, другим пользователям) понять, что делает ваш код, и что вы думаете, когда вы его написали. Представьте, что вы просите о помощи, представьте себе вывод кода отладки или представьте, что вы вернулись, чтобы исправить ошибку через 12 месяцев после последнего касания кода. Что будет лучше и быстрее для вас/других, чтобы понять?

Если ваш код требует, чтобы вы "за каждый год отображали данные", то первое более логично. Если ваше мышление "мне нужно собрать все меры вместе, тогда я их обработаю", затем перейдите к второму варианту. Если вам нужно переупорядочить по годам, перейдите к первому.

Основываясь на вашем примере выше, хотя способ, которым я бы справился с вышеуказанным, возможно:

$array[$year][$month] = $measure;

Вам не нужен конкретный элемент "measure". Или если у вас есть два элемента в месяц:

$array[$year][$month] = array('measure' => $measure, 'value'=>$value);

or

$array[$year][$month]['measure'] = $measure;
$array[$year][$month]['value'] = $value;

Затем вы можете пойти:

for($year = $minYear; $year <= $maxYear; $year++) {  // Or "foreach" if consecutive
    for ($month = 1; $month <= 12; $month++) {
        if (isset($array[$year][$month])) {
             echo $array[$year][$month]['measure'];  // You can also check these exist using isset if required
             echo $array[$year][$month]['value'];
        } else {
             echo 'No value specified'
        }
    }
}

Надеюсь, что это поможет в вашем мышлении.

Ответ 3

Вы делаете это правильно. Вы должны понимать, что PHP не имеет реальных многомерных массивов; то, что вы ищете, представляет собой массив массивов, каждый из которых является одномерным. Основной массив хранит указатели ( "итератор", как вы выразились). Из-за этого первостепенный путь - это единственный разумный способ:

В вашем конкретном примере вы можете представить свой двумерный массив как содержащий набор объектов, каждый из которых имеет значения для 'year', 'month', и 'measure' (плюс первичный ключ, 'owner'). Заполнив основной индекс, вы можете обратиться к каждой строке двумерного массива следующим образом: $myArray[$owner]. Каждое такое значение представляет собой трехэлементный массив с ключами 'year', 'month', и 'measure'. Другими словами, он идентичен вашей оригинальной одномерной структуре данных для той же информации! Вы можете передать его функции, которая имеет дело только с одной строкой таблицы, вы можете легко отсортировать строки $myArray и т.д.

Если бы вы указали свои индексы в обратном порядке, вы не сможете восстановить свои индивидуальные записи. Нет никакой "срезовой" нотации, которая может дать вам целую "колонку" двумерного массива.

Теперь для более широкой перспективы:

Поскольку вы задали свой вопрос в терминах строк и столбцов, обратите внимание, что при первом включении индекса "строка" ваши массивы совместимы с матричной арифметикой. Это огромная победа, если вам нужно делать вычисления с помощью матриц. Нотация базы данных также помещает записи в строки, поэтому делать это обратное будет ненужно усложнять вещи.

C имеет реальные двумерные массивы и массивы указателей. Массивы указателей работают точно так же, как в PHP (хотя допускаются только числовые индексы), и по той же причине: основной индекс выбирает из массива указателей, а младший индекс - просто индекс массива с указателем. Двумерные массивы C работают одинаково: основной указатель находится слева, а смежные местоположения в памяти различаются на одно значение младшего (второго) индекса (за исключением, конечно, в конце строки). Это делает их совместимыми с массивами указателей, поскольку позволяет ссылаться на строку двумерного массива с использованием одного индекса. Например, a[0] - abcd:

        a[.][0] a[.][1] a[.][2] a[.][3]
a[0]:      a       b       c       d    
a[1]:      e       f       g       . 
a[2]:      .       .       .       .    
a[3]:      .       .       .       .    

Система работает без сбоев, потому что основной (строковый) индекс является первым. Fortran имеет реальные двумерные массивы, но имеет главный индекс справа: смежные местоположения в памяти отличаются на одно значение индекса слева (первым). Я обнаружил, что это боль в шее, так как нет подвыражения, которое одинаково сводится к одномерному массиву. (Но у меня есть C-фон, поэтому я, конечно, предвзятый).

Вкратце: вы делаете это правильно, и это возможно не случайно, а потому, что вы узнали, глядя на хорошо написанный код.

Ответ 4

С моей точки зрения, это не вопрос о соглашениях об именах массивов, а о том, как структурировать ваши данные. Значение: какие части информации принадлежат друг другу - и почему? Чтобы ответить на этот вопрос, вы должны смотреть как на читаемость, так и на производительность.

С точки зрения разработчика Java (и вы отметили вопрос также для Java), я не являюсь другом многомерных массивов, поскольку они, как правило, приводят к склонности к ошибкам в акробатике в огромных количествах вложенных for-loops. Чтобы избавиться от второго измерения в вашем массиве, можно создать дополнительные объекты, которые заключают в себе информацию одного столбца.

Теперь необходимо принять решение о том, какие данные должны быть встроены в этот охватывающий объект. В вашем случае ответ прост: набор данных об одном пользователе имеет смысл, список некоррелированных значений для одного свойства для нескольких произвольных пользователей обычно не работает.

Даже если вы не инкапсулируете свои данные в объекты и вместо этого предпочитаете использовать многомерные массивы, вы должны помнить об этих мыслях и видеть последнее измерение массива (в данном случае одного столбца), эквивалентное инкапсулированию объект. Ваша структура данных должна быть полезна на всех уровнях абстракции, и это обычно означает, что вы собрали то, что используется вместе.

Ответ 5

Я считаю, что когда вы используете данные, которые должны храниться как коллективные, лучше хранить их в одном наборе данных (например, ассоциативный массив или объект). Ниже приведены примеры структур, которые могут использоваться для хранения наборов данных.

В PHP, как ассоциативные массивы:

$owner = array(
    'year' => 2012, 'month' => 'July', 'measure' => 3
);

В С#, как хеш-таблицы:

Hashtable owner = new Hashtable();
owner.Add("year", 2012);
owner.Add("month", "July");
owner.Add("measure", 3);

В Java, как хеш-таблицы:

Hashtable owner = new Hashtable();
owner.put("year", new Integer(2012));
owner.put("month", new String("July"));
owner.put("measure", new Integer(3));

В С#/Java, как объекты:

public class Owner {
    public int year;
    public string month;
    public int measure;
}

Owner o = new Owner();
o.year = 2012;
o.month = "July";
o.measure = 3;

В С# и Java преимущество использования объектов над хэш-таблицами заключается в том, что для каждого поля/переменной могут быть объявлены типы переменных (т.е. int или string), что поможет предотвратить ошибки и сохранить целостность данных, поскольку ошибки будут (или предупреждения будут генерироваться), когда вы пытаетесь назначить неверный тип данных в поле.

В PHP я обнаружил, что объекты не имеют реальных преимуществ перед массивами при хранении коллекций данных, потому что тип hinting не позволяет скалярные переменные, что означает, что для проверки/ограничения того, какие данные вводятся в свойстве, требуется дополнительный код, вам нужно написать дополнительный код для объявления класса, вы можете случайно присвоить свои значения неправильному свойству, написав неверное имя (только те, может быть сделано с массивами, поэтому это не помогает с целостностью данных), а итерация/манипуляция свойствами требует также дополнительного кода. Мне также легче работать с ассоциативными массивами при преобразовании в/из JSON в PHP.

На основе кода в вопросе чаще всего вам нужно добавить другое измерение в массив, когда вам нужно получить доступ к данным через одно из полей или некоторые другие критерии (например, сочетание полей или данных, которые поле будет отображаться на).

Здесь хэш-таблицы и ассоциативные массивы более полезны для каждого из языков. Например, если вы хотите организовать владельцев на группы в зависимости от года и месяца, вы можете создать ассоциативные массивы (или хеш-таблицы) для этого.

Следующий пример PHP использует PDO и fetchAll для получения информации из базы данных:

$sth = $dbh->prepare("SELECT year, month, measure FROM owners");
$sth->execute();

$rows = $sth->fetchAll(PDO::FETCH_ASSOC);

$data = array();
foreach ($rows as $row) {
    $year = $row['year'];
    $month = $row['month'];

    if (!isset($data[$year])) {
        $data[$year] = array();
    }

    if (!isset($data[$year][$month])) {
        $data[$year][$month] = array();
    }

    array_push($data[$year][$month], $row);
}

Пример того, как эти данные могут выглядеть как код:

$data = array(
    2011 => array(
        'July' => array(
            array('year' => 2011, 'month' => 'July', 'measure' => 1),
            array('year' => 2011, 'month' => 'July', 'measure' => 3)
        ),

        'May' => array(
            array('year' => 2011, 'month' => 'May', 'measure' => 9),
            array('year' => 2011, 'month' => 'May', 'measure' => 4),
            array('year' => 2011, 'month' => 'May', 'measure' => 2)
        )
    ),

    2012 => array(
        'April' => array(
            array('year' => 2012, 'month' => 'April', 'measure' => 7)
        )
    )
);

Затем вы можете получить доступ к данным с помощью клавиш.

$data[2011]['July'];

// array(
//     array('year' => 2011, 'month' => 'July', 'measure' => 1),
//     array('year' => 2011, 'month' => 'July', 'measure' => 3)
// )

Кроме того, я пытаюсь сохранить мои объекты, которые представляют собой коллекции данных, минимальные, когда я их создаю. Если вы сохраняете свои коллекции в объектах, когда вы начинаете добавлять функции, которые выполняют операции над коллекциями, будет больше кода для поддержки. Бывают случаи, когда это необходимо, например, если вам нужно ограничить, какие значения могут быть сохранены через сеттеры, но если все, что вы делаете, передавая данные от пользователя в базу данных и снова отображая их на экране, тогда обычно нет необходимости в расширенных функциональных возможностях в сборе данных, и имеет смысл иметь функциональность, управляемую в другом месте. Например, классы View могут обрабатывать отображение данных, классы моделей могут обрабатывать извлечение данных, а объекты проверки могут проверять, могут ли данные сохраняться или проверять, могут ли операции выполняться на основе данных в коллекции.

Ответ 6

Я очень редко использую массивы на Java, и то же самое относится к С#. Они являются объектно-ориентированными языками, и вы обычно получаете гораздо лучшую гибкость в долгосрочной перспективе, если вы используете классы и объекты вместо примитивных конструкций, таких как массивы.

Вы, например, выглядите как перекрестная ссылка или поиск, известный как ассоциативный массив. Каким образом вы ставите элементы на самом деле, зависит от того, как вы собираетесь его использовать. Это конструктивное решение, характерное для проблемы. Вы должны спросить себя, с чего вы начнете, и что вы хотите в конечном итоге?

Похоже, вы хотите получать разные типы в зависимости от того, что вы ищете? Месяц - это String, year - Integer и т.д. Это делает массив или HashMap плохим выбором, потому что вызывающий код должен будет угадать тип данных, которые он извлекает. Лучшим вариантом было бы обернуть эти данные в объект, который будет безопасным по типу. Такого рода смысл использования Java и С#.

В этом случае использование объекта даст вам гибкость, чтобы проверить, действительно ли месяц является реальной стоимостью. Например, вы можете создать перечисление, содержащее месяцы, и вернуть экземпляр этого метода getMonth.

Кроме того, у меня нет достаточного количества очков, чтобы оставить комментарий, но я хотел ответить на ответ Dave F. Hashtable - это унаследованный класс из первых дней Java, поэтому его следует избегать. HashMap - рекомендуемая замена, оба реализуют интерфейс Map.

В ранние дни Java классы коллекции были синхронизированы, чтобы сделать их потоковыми (Vector, Hashtable и т.д.). Это сделало эти основные классы необоснованно медленными и затрудняло работу. Если вам нужна синхронизированная карта в эти дни, есть оболочка для HashMap.

Map m = Collections.synchronizedMap(new HashMap(...)); 

Другими словами, нет смысла использовать Hashtable больше, если вы не работаете с устаревшим кодом.

Ответ 7

Собственно, если мы обобщаем все языки программирования, это не просто субъективный вопрос. Производительность обхода массива для многих языков программирования ковыряется, если программист использует неверную индексацию, потому что языки программирования не всегда сохраняют свои массивы в памяти тем же способом. Насколько мне известно, большинство языков - строка-майор или столбцы. Вам нужно знать, что это такое, если вы собираетесь писать программы, требующие хруста высокой производительности.

Пример: C использует row-major, что является обычным способом его выполнения. Когда вы перебираете массив N x N по строкам, вы, вероятно, будете иметь доступ только к памяти N раз, потому что каждая строка загружается вместе. Итак, для первого элемента первой строки вы выйдете в память и вернете всю строку. Для второго элемента первой строки вам не нужно будет выходить в память, потому что строка уже загружена. Однако, если вы решили пойти по столбцу, могут возникнуть проблемы с памятью. Для первого элемента первого столбца вы загрузите всю строку, затем для второго элемента первого столбца вы загрузите следующую строку... и т.д. После того, как вы закончите пространство в кеше, первое строка, вероятно, будет отброшена, чтобы освободить место, и как только вы начнете загружать все элементы в столбце 2, вам придется начинать все заново. Это не проблема в Fortran; скорее, вы захотите сделать это так, потому что весь столбец загружается сразу, а не вся строка. Надеюсь, это имело смысл. См. Статью Wikipedia, связанную с выше, для более наглядного объяснения.

Для большинства программ самым приоритетным разработчиком должен быть чистый код. Но знание того, как этот язык обрабатывает память, может быть существенным в тех случаях, когда производительность является ключевой. Хороший вопрос.