КАК ВНУТРИ СОЕДИНИТЬ правильное ЗНАЧЕНИЕ из 2 значений в таблице, где 1 является правильным

Фиксированный размер

1. Передача по ссылке

template 
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

В C ++ передача массива по ссылке без потери информации о размерах, вероятно, является самой безопасной, так как не нужно беспокоиться о том, что вызывающий абонент передает неправильное измерение (флаги компилятора при несоответствии). Однако это невозможно с динамическими (freestore) массивами; он работает только для автоматических ( обычно стек-живых ) массивов, т. е. размерность должна быть известна во время компиляции.

2. Pass by pointer

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

С-эквивалент предыдущего метода передает массив по указателю. Это не следует путать с передачей по разлагаемому типу указателя массива (3), который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Как и (1), используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции адрес массива должен быть передан process_2d_array_pointer(&a), а не адрес первого элемента путем распада process_2d_array_pointer(a).

Размер переменной

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

Следует помнить, что нет такой вещи, как передача массива непосредственно функции в C [в то время как в C ++ они могут быть переданы как ссылка (1)]; (2) передает указатель на массив, а не сам массив. Всегда передавать массив as-is становится операцией копирования-указателя, которая облегчается природой массива для разложения в указатель .

3. Pass by (value) указатель на затухающий тип

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Хотя разрешено int array[][10], я бы не рекомендовал его над указанным выше синтаксисом, поскольку приведенный выше синтаксис дает понять, что идентификатор array является единственным указателем на массив из 10 целых чисел, в то время как этот синтаксис выглядит , как будто это 2D-массив, но является тем же самым указателем на массив из 10 целых чисел. Здесь мы знаем количество элементов в одной строке (т. Е. Размер столбца, здесь 10), но количество строк неизвестно и, следовательно, должно быть передано в качестве аргумента. В этом случае есть определенная безопасность, поскольку компилятор может указывать, когда передается указатель на массив со вторым измерением, не равным 10. Первое измерение является изменяющейся частью и может быть опущено. См. здесь обоснование о том, почему разрешено только первое измерение.

4. Пропустить указатель на указатель

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Опять есть альтернативный синтаксис int *array[10], который совпадает с int **array. В этом синтаксисе [10] игнорируется, поскольку он распадается на указатель, тем самым становясь int **array. Возможно, это всего лишь сигнал вызывающему, что переданный массив должен иметь не менее 10 столбцов, даже если требуется подсчет строк. В любом случае компилятор не указывает на какие-либо нарушения длины и размера (он проверяет, является ли переданный тип указателем на указатель), поэтому для определения значения параметра строки и столбца требуется значение параметра

Примечание: (4) является наименее безопасным вариантом , поскольку он вряд ли имеет проверку типа и наиболее неудобен. Нельзя законно передать 2D-массив этой функции; C-FAQ осуждает обычное обходное решение для выполнения int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);, поскольку оно может потенциально привести к неопределенному поведению из-за сглаживания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, то есть нам нужен дополнительный (суррогатный) массив указателей, каждый из которых указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для того, чтобы выполнить ту же работу, что и вышеупомянутые методы, которые более безопасны, чище и, возможно, быстрее.

Вот программа драйвера для проверки вышеперечисленных функций:

#include 

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
3
задан GMB 3 March 2019 в 22:57
поделиться

1 ответ

Если предположить, что отношения являются односторонними, от U1 до U2, то все просто:

select u.*
from relationships r
inner join users u on r.u2_fk = u.id
where r.u1_fk = ?

Я понятия не имею, что означает действие или статус.

Что-то, что сделало бы это более понятным, это если вы сделаете свое поле дБ более описательным в таблице отношений: то есть user_id и friend_id. Тогда становится очевидным, что вы хотите присоединить файл Relations.friend_id к user.id и отфильтровать по параметру Relations.user_id

Затем вы можете дополнительно выполнить фильтрацию по статусу и т. Д. добавив желаемые результаты, я вижу, что вы хотите включить в результаты как u1_fk, так и u2_fk. Это, конечно, можно сделать, но вы делаете это гораздо более запутанным, ИМО.

Я бы предложил, чтобы, когда дружба была принята, создала новую линию для нового друга. Таким образом, когда вы хотите найти всех друзей для пользователя, вам нужно только сопоставить u1_fk (то есть, user_id), а другом всегда будет u2_fk (friend_id).

Затем каждый друг может независимо контролировать, как они хотят общаться, т.е. вы можете заблокировать меня или я могу заблокировать вас. - мои два цента стоят. Заставьте свою ассоциацию делать одно.


Может быть полезно взглянуть на схему:

User    Relationship    User
           id
id ---->  u1_fk
          u2_fk <------- id

Это было бы легко понять. Но то, что вы хотите из этого, больше похоже на

User    Relationship    User
           id
 id -----> u1_fk <------ id
     \---> u2_fk <---/

Итак, для начала, я смотрю на это с точки зрения фильтрации по отношениям, а затем добавляю информацию о пользователях по мере их соответствия:

select r.id, r.u1_fk, r.u2_fk, 
       coalesce(u1.user_id, u2.user_id) userid,
       coalesce(u1.username, u2.username) username,
       coalesce(u1.user_picture, u2.user_picture) picture
from relationship r
left join user u1 on r.u1_fk = u1.id
left join user u2 on r.u1_fk = u2.id
where u1_fk = ? or u2_fk = ?

но это означает, что вам придется объединять поля из u1 и u2.

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

Однако, что происходит если у вас есть:

id    u1_fk    u2_fk
        1        2
        2        1

Я думаю, что оба решения дадут вам дублирующий список.

Вот почему я настоятельно рекомендую вам переименовать (по крайней мере, в вашей голове) u1_fk и u2_fk в user_id и friend_id.

0
ответ дан Tim Morton 3 March 2019 в 22:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: