LINUX.ORG.RU

Проверка большого числа условий на си

 


1

2

Сейчас я пишу такой код:

	if( phonebookEntry->index < 0 && phonebookEntry->telNo && !phonebookEntry->name ){
		//только телефон
	}else if( phonebookEntry->index < 0 && phonebookEntry->telNo && phonebookEntry->name ){
		//телефон и имя
	}else if( phonebookEntry->index >= 0 && !phonebookEntry->telNo && !phonebookEntry->name ){
		//только индекс
	}else if( phonebookEntry->index >= 0 && phonebookEntry->telNo && !phonebookEntry->name ){
		//индекс и телефон
	}else if( phonebookEntry->index >= 0 && phonebookEntry->telNo && phonebookEntry->name ){
		//индекс, телефон и имя
	}else{
		//недопустимое сочетание
	}

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

	unsigned int condition = 0;
	
	if( phonebookEntry->index >= 0 ) condition |= 1;
	if( phonebookEntry->telNo      ) condition |= 1 << 1;
	if( phonebookEntry->name       ) condition |= 1 << 2;

И дальше переменную condition запихнуть в switch или вообще использовать в качестве индекса в массиве указателей? Мой случай ещё пограничный, но если добавить ещё один параметр, то вариантов уже будет 16! Короче, степень двойки. Я один такой упоротый или такое где-нибудь применяется?

★★★

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

Собстна ты открыл для себя https://en.wikipedia.org/wiki/Mask_(computing)

Bfgeshka ★★★★★
()

имхо есть две крайности

первая мы определяем какое из 2**n состоянии мы находимся посредством n тестов ( всёж обычно пространство состояний меньше 2вn ибо обычно условия не абсолютно независимы) и действуем

вторая каждый раз тестируя некоторое очередное условие мы меняем состояние тем самым после n тестов мы оказываемся в конечном состоянии

имхо второй подход более императивный и обычно код проще - но есть связи по «памяти»

первый же подход ацки ветвист но типо очевидно какие действия в каком (начальном)состоянии

qulinxao3 ★☆
()
Ответ на: комментарий от u5er

Самое обычное применение для них, какбе - компоновка условий.

Мой случай ещё пограничный, но если добавить ещё один параметр, то вариантов уже будет 16!

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

Bfgeshka ★★★★★
()

Первый код понятный и читабельный. Второй код непонятный ещё на этапе пролога и будет 100% нечитаемый на этапе условий. Честно говоря не вижу в этом никакого смысла.

Единственное, что я бы сделал, это вынес бы выражения phonebookEntry->index, phonebookEntry->telNo, phonebookEntry->name в переменные index, telNo, name перед этими if-ами:

  int8_t index = phonebookEntry->index;
  const char *telNo = phonebookEntry->telNo;
  const char *name = phonebookEntry->name;
  if (index < 0 && telNo != NULL && name == NULL) {
    // только телефон
  } else if (index < 0 && telNo != NULL && name != NULL) {
    // телефон и имя
  } else if (index >= 0 && telNo == NULL && name == NULL) {
    // только индекс
  } else if (index >= 0 && telNo != NULL && name == NULL) {
    // индекс и телефон
  } else if (index >= 0 && telNo != NULL && name != NULL) {
    // индекс, телефон и имя
  } else {
    //недопустимое сочетание
  }
vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от Bfgeshka

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

u5er ★★★
() автор топика
Ответ на: комментарий от vbr

Речь идёт не про читаемость, а про то, как поступить, если условий будет не 3, а хотя бы 5. Это уже 32 варианта. И вопрос стоит так: прописывать все 32 случая оператором if с пятью проверками, либо на входе утромбовать всё в маску и передать ещё либо в switch, либо вызвать функцию из массива указателей, либо ещё что-нибудь.

u5er ★★★
() автор топика
Ответ на: комментарий от u5er

Можешь рассмотреть вариант выноса общие проверок наверх, ещё и работать быстрей будет.

if (index < 0) {
  if (telNo != NULL) {
    if (name == NULL) {
      // только телефон
    } else { // name != NULL
      // телефон и имя
    }
  } else { // telNo == NULL
    //недопустимое сочетание
  }
} else { // index >= 0
  if (telNo == NULL) {
    if (name == NULL) {
      // только индекс
    } else { // name != NULL
      //недопустимое сочетание
    }
  } else { // telNo != NULL
    if (name == NULL) {
      // индекс и телефон
    } else { // name != NULL
      // индекс, телефон и имя
    }
  }
}

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

vbr ★★★★★
()

Фу, как завоняло сями и несущестующими проблемами.

Первый пример - никогда, никогда не пиши так без выноса повторяющихся условий в bool переменные.

Второй пример - никогда, никогда не пиши так, используй именованные константы (if (phonebookEntry->telNo) { flags |= HAS_PHONE; }).

Так-то ЗДЕСЬ действительно напрашивается switch, но вообще ты решаешь несуществующую проблему - почему не дать пользователю заполнять то что он считает нужным так как он считает нужным, и зачем и как именно ты собрался уникальным образом обрабатывать каждую возможную комбинацию?

Если на наличие этих условий есть какие-то объективные причины (требуется уникальность, например), то условие можно сильно упростить, здесь это всего лишь has_phone || has_index. Итого:

bool has_phone = phonebookEntry->telNo;
bool has_index = phonebookEntry->index >= 0;

if (has_phone || has_index) {
    // ...
} else {
    // недопустимое сочетание, нужно заполнить телефон и/или индекс
}

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

if (phonebookEntry->telNo || phonebookEntry->index >= 0) {
    /// ...
} else {
    // нужно заполнить телефон и/или индекс
}

As easy as that.

anonymous
()

unsigned int condition...

А что, если так:

enum phone_book_entry_states { NONE = 0, IS_INDEX = 1, IS_NUMBER = 2, IS_NAME = 4 };

if (phonebookEntry -> index != NULL) state = ~NONE;
if (phonebookEntry -> telNo) state &= IS_NUMBER;
if (phonebookEntry -> name) state &= IS_NAME;

Выглядит более читаемо, кмк.

yars068 ★★★★★
()
Последнее исправление: yars068 (всего исправлений: 2)
Ответ на: комментарий от vbr

Ты не поверишь, но это был изначальный вариант ;) Если тут расписать всю логику, то код превращается в нечитаемую мешанину.

Кстати, обратил внимание, что на трёх битах можно получить 8 вариантов? Вот как раз 7 из них у тебя и получились. За исключением того, когда все биты == 0 (что тоже недопустимо в данном коде).

u5er ★★★
() автор топика
Ответ на: комментарий от anonymous

но вообще ты решаешь несуществующую проблему - почему не дать пользователю заполнять то что он считает нужным так как он считает нужным, и зачем и как именно ты собрался уникальным образом обрабатывать каждую возможную комбинацию?

Потому что каждая ситуация уникальна - это раз. Не каждая комбинация допустима - это два. И это не проблема, а задача - это три.

u5er ★★★
() автор топика
Ответ на: комментарий от vbr

Единственное, что я бы сделал, это вынес бы выражения phonebookEntry->index, phonebookEntry->telNo, phonebookEntry->name в переменные index, telNo, name перед этими if-ами

Можно еще сахарку подсыпать с помощью макросов или static inline функций типа hasIndex(phonebookEntry), hasTelNo(phonebookEntry) и т.п.

alx777 ★★★
()

Чуть в сторону, но всё же. Можно вот так фигулькнуть, полупсевдокод, но смысл думаю ясен

#include <stddef.h>

enum options
{
   PHONE, PHONE_NAME, INDEX, INDEX_PHONE, INDEX_PHONE_NAME, BAD
};

enum options_stats
{
   HAS_INDEX = 1, NOP_INDEX = 0,
   HAS_PHONE = 1, NOP_PHONE = 0,
   HAS_NAME  = 1, NOP_NAME  = 0,
};

int lookup[2][2][2] =
{
   [NOP_INDEX][HAS_PHONE][NOP_NAME] = PHONE;
   [NOP_INDEX][HAS_PHONE][HAS_NAME] = PHONE_NAME;
   [HAS_INDEX][NOP_PHONE][NOP_NAME] = INDEX;
   [HAS_INDEX][HAS_PHONE][NOP_NAME] = INDEX_PHONE;
   [HAS_INDEX][HAS_PHONE][HAS_NAME] = INDEX_PHONE_NAME;
   /*ну и BAD значения прописать*/
}

int a = ( phonebookEntry->index >= 0 );
int b = ( phonebookEntry->telNo  != NULL    );
int c = ( phonebookEntry->name   != NULL    );

switch(lookup[a][b][c])
{
    case PHONE: ... break;
    case PHONE_NAME: ... break;
    case INDEX: ... break;
    case INDEX_PHONE ... break;
    case INDEX_PHONE_NAME ... break;
    default: /*недопустимое почетание*/ 
}

Я типа такого использую (не помню уже где, рыть надо) только там заместо свича, в лукап табличку указатели на функци сую ну и просто lookup[a][b][c](args) дёргаю и всё, но у меня там параметры одни и теже, удобна. Для произвольного кода уже лучше свич или типа того, тут уже без разницы. Правда лукап табличка может раздуться, но зато она конечный тратата

LINUX-ORG-RU ★★★★★
()

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

напиши шаблонную constexpr-функцию для вычисления маски и функции для сравнения.

ckotctvo
()

Ты немного не там видишь тему для размышлений.

Конкретно с вопросом из темы - первый вариант выглядит лучше, но «phonebookEntry» конечно лучше убрать и сделать вместо него что-нить однобуквенное.

Думать надо лучше о другом: такие ситуации вообще не должны часто возникать. А тем более такие:

как поступить, если условий будет не 3, а хотя бы 5. Это уже 32 варианта

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

Если ты покажешь что именно внутри if-ов из темы накодено - возможно тоже сможем найти как это всё переделать без перебора всех вариантов.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

сделать вместо него что-нить однобуквенное.

Ты ко мне с этой чушью не лезь, пожалуйста. Я к подобным наименованиям переменных пришёл после того, как настрадался порядком. Я раньше писал код точно так же, как и ты и топил за компактность. Со временем пришёл к однозначному выводу, что на выходе получается write-only код. Напоминаю, что я пишу на языке си, а не на языке ассемблера - тут можно давать понятные имена функциям и переменным и я пользуюсь этим благом.

Думать надо лучше о другом: такие ситуации вообще не должны часто возникать.

Так они и не возникают часто ;) Это второй раз за всю историю моего программирования. Причём в прошлый раз тоже было 3 проверки.

u5er ★★★
() автор топика
Ответ на: комментарий от firkax

Если ты покажешь что именно внутри if-ов из темы накодено - возможно тоже сможем найти как это всё переделать без перебора всех вариантов.

ПРЕДУПРЕЖДАЮ СРАЗУ: меня ПОЛНОСТЬЮ устраивает этот код ;)

///   PREPARE AT CMD
	
	if( phonebookEntry->index < 0 && phonebookEntry->telNo && !phonebookEntry->name ){
		
		//только телефон
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=,\"%s\"\r", phonebookEntry->telNo );
		
	}else if( phonebookEntry->index < 0 && phonebookEntry->telNo && phonebookEntry->name ){
		
		//телефон(, тип) и имя
		if( phonebookEntry->telNo[0] == '+' ) type = 145;
		else                                  type = 129;
		
		res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
		if( res < 0 ) return -1;
		
		res = snprintf(
			gsmService->atCmdBuffer,
			AT_CMD_BUFFER_SIZE,
			"AT+CPBW=,\"%s\",%i,\"%s\"\r",
			phonebookEntry->telNo,
			type,
			gsmService->codecBuffer
		);
		
	}else if( phonebookEntry->index >= 0 && !phonebookEntry->telNo && !phonebookEntry->name ){
		
		//только индекс
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=%i\r", phonebookEntry->index );
		
	}else if( phonebookEntry->index >= 0 && phonebookEntry->telNo && !phonebookEntry->name ){
		
		//индекс и телефон
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=%i,\"%s\"\r", phonebookEntry->index, phonebookEntry->telNo );
		
	}else if( phonebookEntry->index >= 0 && phonebookEntry->telNo && phonebookEntry->name ){
		
		//индекс, телефон(, тип) и имя
		if( phonebookEntry->telNo[0] == '+' ) type = 145;
		else                                  type = 129;
		
		res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
		if( res < 0 ) return -1;
		
		res = snprintf(
			gsmService->atCmdBuffer,
			AT_CMD_BUFFER_SIZE,
			"AT+CPBW=%i,\"%s\",%i,\"%s\"\r",
			phonebookEntry->index,
			phonebookEntry->telNo,
			type, 
			gsmService->codecBuffer
		);
		
	}else{
		//недопустимое сочетание
		return -1;
	}
	
	if( res < 0 ) return -1;
u5er ★★★
() автор топика
Ответ на: комментарий от u5er

Ты ко мне с этой чушью не лезь, пожалуйста

Но в твоём коде это phonebookEntry занимает примерно половину байт всего кода, это разве нормально?

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

В ассемблере тоже можно имена давать.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Но в твоём коде это phonebookEntry занимает примерно половину байт всего кода, это разве нормально?

Байт? Ты, наверное имел в виду, строки? Да, имя длинное, но оно однозначно даёт понять о том, что это такое. Если использовать одно-двухбуквенные сокращения, то это снижает понятность кода. Поэтому я предпочитаю многословные названия.

И это, кстати, одтн из самых сложных аспектов программирования для меня - давать лаконичные и понятные имена.

u5er ★★★
() автор топика
Ответ на: комментарий от u5er

Как и ожидалось, общие места вполне есть. В начале уберём отдельные ветки по наличию/отсутствию name

///   PREPARE AT CMD
	
	if( phonebookEntry->index < 0 && phonebookEntry->telNo){
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=,\"%s\"", phonebookEntry->telNo );
		if( res < 0 ) return -1;
                if(phonebookEntry->name) {
			if( phonebookEntry->telNo[0] == '+' ) type = 145;
			else                                  type = 129;
			res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
			if( res < 0 ) return -1;		
			res = snprintf(
				gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer),
				AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer),
				",%i,\"%s\"",
				type,
				gsmService->codecBuffer
			);
			if( res < 0 ) return -1;
                }
                res = snprintf(gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer), AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer), "\r");		
	}else if( phonebookEntry->index >= 0 && !phonebookEntry->telNo && !phonebookEntry->name ){
		
		//только индекс
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=%i\r", phonebookEntry->index );
		
	}else if( phonebookEntry->index >= 0 && phonebookEntry->telNo){
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=%i,\"%s\"", phonebookEntry->index, phonebookEntry->telNo );
		if( res < 0 ) return -1;
                if(phonebookEntry->name) {
			if( phonebookEntry->telNo[0] == '+' ) type = 145;
			else                                  type = 129;
			res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
			if( res < 0 ) return -1;		
			res = snprintf(
				gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer),
				AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer),
				",%i,\"%s\"",
				type,
				gsmService->codecBuffer
			);
			if( res < 0 ) return -1;
                }
                res = snprintf(gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer), AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer), "\r");
	}else{
		//недопустимое сочетание
		return -1;
	}
	
	if( res < 0 ) return -1;
Видим что ветки с индексом и без индекса оказались почти одинаковые, объединяем их тоже
///   PREPARE AT CMD
	if(phonebookEntry->telNo){
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=");
		if( res < 0 ) return -1;
		if(phonebookEntry->index >= 0) {
			res = snprintf( gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer), AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer), "%i", phonebookEntry->index);
			if( res < 0 ) return -1;
		}
		res = snprintf( gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer), AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer), ",\"%s\"", phonebookEntry->telNo );
		if( res < 0 ) return -1;
                if(phonebookEntry->name) {
			if( phonebookEntry->telNo[0] == '+' ) type = 145;
			else                                  type = 129;
			res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
			if( res < 0 ) return -1;		
			res = snprintf(
				gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer),
				AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer),
				",%i,\"%s\"",
				type,
				gsmService->codecBuffer
			);
			if( res < 0 ) return -1;
                }
                res = snprintf(gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer), AT_CMD_BUFFER_SIZE-strlen(gsmService->atCmdBuffer), "\r");		
	}else if( phonebookEntry->index >= 0 && !phonebookEntry->telNo && !phonebookEntry->name ){
		//только индекс
		res = snprintf( gsmService->atCmdBuffer, AT_CMD_BUFFER_SIZE, "AT+CPBW=%i\r", phonebookEntry->index );
	}else{
		//недопустимое сочетание
		return -1;
	}
	
	if( res < 0 ) return -1;
По-моему, получившийся код намного нагляднее (ещё бы имена переменных исправить...). Решены сразу несколько проблем:

1) осталось всего 3 ветки этого каскадного if-а вместо шести, да и строк стало поменьше

2) убран копипастенный код, и теперь, если захочешь внести изменения, их придётся вносить в одно место а не в несколько

3) код стал заметно нагляднее: видно, что на самом деле у нас есть два разрешённых варианта - «есть телефон» и «есть индекс без всего остального», при этом у варианта «есть телефон» есть два опциональных параметра - индекс и имя.

Ещё я бы рекомендовал заменить snprintf на что-то более нормальное, чтобы не писать каждый раз этот флуд со strlen-ами и не дублировать везде AT_CMD_BUFFER_SIZE. Чтобы выглядело примерно так:

buffer_struct = () {.size=AT_CMD_BUFFER_SIZE, .buf=gsmService->atCmdBuffer /* .len=0 можно не писать */ }; /* инициализация */
append_printf(&buffer_struct, "%i", index); /* добавление текста */

Можно наверно ещё и ветку «только индекс» немного объединить с остальным - начало AT-команды то везде одинаковое, можно его ещё до if-ов вывести, и индекс, если есть, тоже вывести там, а дальше только проверить корректность данных.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от u5er

Нет, я имел ввиду байт. Правда я это оценил когда были только if-ы без кода внутри них (тот код что вверху темы). Ну то есть название переменной занимает 14 байт, повторяется там 15 раз, то есть всё это занимает 210 байт. При этом вся простыня занимает 673 байта. Ну ладно, не половину, а чуть меньше трети, всё равно много.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Что за K&R if else?

Да про скобки и отступы.

то у него оно идеальное как раз, все остальные стили ужасны.

По мне так многоуровневые if else с ним сложно читать, я больше BSD стиль (Allman) предпочитаю, но с рядом изменений для большей компактности кода.

Dr64h ★★★★
()
Последнее исправление: Dr64h (всего исправлений: 1)
Ответ на: комментарий от firkax

gsmService->atCmdBuffer+strlen(gsmService->atCmdBuffer)

Вот это была одна из моих идей, только я не пошёл по этому пути. Я думал генерировать AT-команду поэтапно. Сначала напечатать в буфер «AT+CPBW=», если есть индекс, вписать его, если есть номер, вписать его, если есть имя, перекодировать и вписать его, заверчить строку «\r», но я по нему не пошёл.

И как раз в твоём коде наглядная причина - у тебя «многоэтажные» проверки. Более того, у тебя печать идёт частями внутри этих проверок. Я тебе отвечаю, что если там допустить опечатку в синтаксисе AT-команды, то я затрахаюсь это отлаживать.

Мне такой код воспринимать тяжелее, чем если я просто разделю код на 5 участков для каждого случая. В моём же коде для каждого конретного случая весь код находится конкретно внутри одного единственного блока. Да, есть дублирование, но и от него можно избавиться, например, дописав такой блок перед всем кодом:

if( phonebookEntry->telNo && phonebookEntry->name ){
	
	if( phonebookEntry->telNo[0] == '+' ) type = 145;
	else                                  type = 129;
		
	res = packUtf82Ucs2( phonebookEntry->name, gsmService->codecBuffer, CODEC_BUFFER_SIZE );
	if( res < 0 ) return -1;
}

Тогда внутри каждого блока останется только печать.

Особой производительности тут не требуется, поэтому я позволил себе это позволить (извините за каламбур).

u5er ★★★
() автор топика

Да вполне себе. Тут по-моему вообще вместо излишних if просто бы сразу класть результат вычисления в нужный бит.

condition |= (phonebookEntry->index >= 0) << 0;
condition |= (phonebookEntry->telNo != 0) << 1;
condition |= (phonebookEntry->name != nullptr) << 2;
Или завернуть в структуру с именованными битовыми полями:
typedef struct {
	char is_index : 1;
	char is_telNo : 1;
	char is_name  : 1;
} flags_t;

flags_t check_entry (phonebookEntry_t *entry) {
	flags_t flags;
	
	flags.is_index = entry->index >= 0;
	flags.is_telNo = entry->telNo != 0;
	flags.is_name  = entry->name != nullptr;
	
	return flags;
}

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 5)
Ответ на: комментарий от u5er

Если тут расписать всю логику, то код превращается в нечитаемую мешанину.

Нечитаемая мешанина в голове читающего.

Вот так ещё не предлагали:

if (index < 0)
  if (telNo != NULL)
    if (name == NULL)
      goto PHONE_ONLY;
    else 
      goto PHONE_NAME;
  else
    goto BAD;
else
  if (telNo == NULL)
    if (name == NULL)
      goto INDEX_ONLY;
    else
      goto BAD;
   else
     if (name == NULL)
       goto PHONE_INDEX;
     else
       goto PHONE_INDEX_NAME;

:PHONE_ONLY
// только телефон
goto END

:PHONE_NAME
// телефон и имя
goto END

:INDEX_ONLY
// только индекс
goto END

:PHONE_INDEX
// индекс и телефон
goto END

:PHONE_INDEX_NAME
// индекс, телефон и имя
goto END

:BAD
//недопустимое сочетание
:END

Psilocybe ★★★★★
()
Ответ на: комментарий от Dr64h

Немного не в тему, но вам реально удобно читать все эти if else ветвления в K&R стиле?

Я не разбираюсь в названиях стилей. То, как я это пишу, это стиль, принятый в Java. Я к нему привык и применяю его во всех языках с похожим синтаксисом. Он компактный и вполне читаемый. В своих проектах я использую clang-format с пресетом Google, он примерно так же форматирует. Мне он не вполне нравится, но это лучшее из встроенных пресетов, а крафтить свой - до такого я пока не дошёл. А вообще идеальным считаю стиль, которым форматирует JavaScript инструмент Prettier. Я, к сожалению, на JavaScript пишу редко, но у меня это всегда вызывает эстетическое наслаждение.

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

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

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vbr

Вообще нет. Блок внутри if выполняется, если условие истинно, то есть, выражение в скобках после if имеет ненулевое значение. Поэтому

if (condition) {
    actions();
  }

необходимо и достаточно. Как только condition обратится в нуль, оно перестанет выполняться.

yars068 ★★★★★
()
Ответ на: комментарий от yars068

А ещё можно в одну строку всё записать, убрать «лишние» фигурные скобки, использовать однобуквенные идентификаторы и другие способы запутать код. Можно, но не нужно. Если, конечно, код не готовится для соревнований по обфускации.

vbr ★★★★★
()
Ответ на: комментарий от vbr

Это всë уж точно не нужно, хоть компилятор и проглотит. Но из-за одного оператора ставить фигурные скобки я тоже не буду. Код должен легко читаться и не быть запутанным.

yars068 ★★★★★
()
Последнее исправление: yars068 (всего исправлений: 1)

Я один такой упоротый или такое где-нибудь применяется?

на Си надо писать не так, как кем-то там принято, а так как кажется лучше лично тебе в конкретной ситуации.

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

Lrrr ★★★★★
()
Ответ на: комментарий от Psilocybe

Нечитаемая мешанина в голове читающего.

То есть у меня, читающего этот код, спустя некоторое время ;)

Вот так ещё не предлагали:

Самый паршивый вариант. Я уже писал подобный код и знаешь, что я тебе скажу? Кокнкретно в этом случае ты пытаешь заменить вызов функций операторами goto. Я уже сам лично погорел на таком, написав немного кода в таком стиле. Особенно «порадовал» мой редактор текста, который я написал в виде одной функции на примерно 1500 строк, с операторами goto в ней. Код получился настолько лютый, что разрулить его вообще никак. Хорошо, что он хотя бы работает ;)

Так что спасибо, но нет. Да ты и сам можешь в этом убедиться - просто замени операторы goto на вызовы функций и сравни. Потом расскажешь о впечатлениях ;)

u5er ★★★
() автор топика
Последнее исправление: u5er (всего исправлений: 1)

вариантов уже будет 16

Флаги (cpu) - сколько бит, сколько вариантов?

Логические функции,таблица истинности, минимизация, карты Карно…

anonymous
()
Ответ на: комментарий от AZJIO

А зачем битовые флаги создавать о наличии поля?

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

u5er ★★★
() автор топика
Ответ на: комментарий от vbr

!= NULL

Вот же любитель замусоривать код.

Это не замусоривание кода, это единственный разумный способ писать код.

Я когда начинал, то тоже "не равно ", но потом решил, а зачем приравнивание, что есть false, а что true? Если false это только для 0, а всё остальное true, то зачем излишнее сравнение, особенно для интерпретируемых языков, в которых нет оптимизации и этот код не превращается в оптимизированный после компиляции.

AZJIO
()
Ответ на: комментарий от AZJIO

Да. Второй код глянь. Переменная condition пойдёт в switch, else if или в качестве индекса массива функий, например. То есть проверка будет всего одна, либо её вообще не будет в случае массива функций.

u5er ★★★
() автор топика
Ответ на: комментарий от u5er

Это позволит заменить проверку трёх условий на проверку одного условия.

Замена дерева условий (дерева принятия решения) на линейный switch, или каскад if … else if … else if …

anonymous
()