Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

Какие стили лучше задавать по умолчанию?

#css #вёрстка


При просмотре туториалов, связанных с версткой заметил, что часто перед началом работы,
авторы задают какие-то стили по умолчанию, переопределяют дефолтные стили браузера.
Собственно, возникло пару вопросов по этому поводу:


Зачем это делается?
Стили каких элементов лучше переопределить в самом начале?
Где можно об этом почитать поподробнее и найти код? Гуггл мне не очень помог, каюсь,
может плохо гугглил.

    


Ответы

Ответ 1



Зачем это делается? По умолчанию, в браузерах есть встроенные определения для HTML-элементов. Например, у и

есть margin, у

    и
      есть padding и list-style, ну и так далее. В каждом браузере эти предустановленные значения могут быть разными, соответственно, normalize.css или reset.css нужны для того, чтобы стартовать с одинаковой базы. Различие их состоит в том, что normalize.css приводит все элементы к одинаковому стилю, а reset.css сбрасывает всё полностью в ноль. Лично мне больше нравится использовать reset.css, потому что, как правило, все элементы потом всё равно переопределяются и для каждого проекта формируется некое подобие UI kit. Но если вы верстаете страницу без формирования определения всех элементов, то, возможно, вам подойдет normalize.css — так вам не надо будет задавать отступы между параграфами и заголовками, переопределять списки и т. д. Стили каких элементов лучше переопределить в самом начале? Тут всё упирается в то, какую стратегию вы выбрали :) Мой необходимый набор: *, *:after, *:before { box-sizing: border-box; margin: 0; padding: 0; } Где можно об этом почитать поподробнее и найти код? Хорошая статья на HTML-академии и на Хабре. Код: reset.css, normalize.css. P.S. reset будущего будет выглядеть примерно так: * { all: unset; } head { display: none; }

      Ответ 2



      Есть два основных подхода - reset.css и normalize.css. Остальное - в google.com

      Ответ 3



      html, body { margin: 0; padding: 0; } Остальное - ручки)

      Ответ 4



      Делается для того, чтобы у всех браузеров привести дефолтные стили к какому-то номиналу, и получить более ожидаемое поведение от элементов на странице и не было всевозможных "призрачных" отступов в каком-нибудь фаерфоксе, и корректного отображения в хроме(например). Переопределяются в основном ссылки, списки, отступы внутренние и внешние у body, div'a, поля ввода, по большому счету, все, что используется в проекте, по хорошему необходимо "обнулить". Гугли всевозможные reset.css. Например, как можно видеть на скрине, в разных браузерах стили рендерятся по разному. В данном случае, FF добавил отступ перед тэгом p. Во избежание таких случаев и применяются сбросы стилей. Немного советов: Сброс стилей - отдельным файлом, включаемым в документ в самом начале. Лучше индивидуальным элементам расписывать сбросы стилей отдельно, дабы достичь максимального контроля над внешним видом страницы. Из минусов - разве что, что-нибудь забыть. Сброс не обязательно приводить к нулевым значениям, можно сразу указать те, что по дефолту будут использоваться в Вашем проекте. Написанное выше является выдержкой из статьи с хабра, отсюда, .

      Ответ 5



      Используйте normalize.css популярная и проверенная библиотека для нормализации стилей в разных браузерах. А дальше используйте сброс(задавать по умолчанию) стилей по вашему вкусу.

Как печатать каждый n-ый элемент массива в ruby?

#ruby


Как печатать каждый n-ый элемент массива в ruby?
    


Ответы

Ответ 1



Например, вот так: arr = [1, 2, 3, 4, 5, 6] n = 3 for i in (n-1..arr.length - 1).step(n) puts arr[i] end Или же так: (n-1..arr.length - 1).step(n) do |i| puts arr[i] end Или ещё компактнее: (n-1..arr.length - 1).step(n) { |i| puts arr[i] } Вывод: 3 6

Ответ 2



Например так: n = 5 (1..40).step(n) {|y| puts y} Будет печатать каждый 5-й элемент, начиная с 1-го.

Ответ 3



В функциональном стиле... Возьму вот эти данные для примера: a = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] n = 2 # каждый 2-й (n-1...a.length).step(n) .map(&a.method(:[])) .each(&method(:puts)) # 9 # 7 # 5 # 3 # 1 n-1...a.length это интересующий нас диапазон индексов, первый из которых обозначает первый элемент интересующего нас результата. Заметьте, что точек три, а значит указанная правая граница в него не входит. В конце концов, a[a.length] всегда nil. (n-1...a.length).step(n) пропускает все индексы из диапазона, кроме каждого n-го. Остаются только те, что нужны. Осталось найти способ преобразовать коллекцию индексов в коллекцию элементов. Есть у рубистов известный трюк с Symbol#to_proc: если всё, что должен делать блок, это вызвать у переданного в него объекта какой-то метод, то вместо написания этого блока руками можно передать в аргументы &:метод в качестве блока. (1..2).map { |i| i.to_s } (1..2).map(&:to_s) # <= то же самое Но здесь этот трюк не работает, поскольку метод надо вызвать не у переданного объекта. Нужен другой трюк. Чуть менее известный. Тут-то и можно воспользоваться method'ом: он возвращает Proc-совместимые объекты методов, всё ещё привязанных к своим владельцам. Амперсанд (&) при вызове метода работает со всем, что имеет метод to_proc). С помощью .map(&a.method(:[])) коллекция индексов преобразуется в коллекцию значений из a по этим индексам. А с помощью .each(&method(:puts)) эта коллекция выводится на экран. На самом деле, решение совершенно непрактичное, но показывает не самые известные детали языка.

Ответ 4



[1,2,3,4,5,6].each_with_index {|val, index| puts val if index % 2 == 0}

Как задать ограничения на параметры шаблонных классов?

#cpp #шаблоны_с++


Есть абстрактный класс M и классы A, B и C, для которых М - родительский класс, и
в них реализованы все абстрактные методы класса М.
Нужно создать шаблонный класс

template  class S{ ... };


в котором качестве шаблона используются классы A, B или C.
Можно как-то указать, что M должен быть родительским классом для Т?
    


Ответы

Ответ 1



Вас спасет std::is_base_of: struct M {}; struct A: public M {}; struct B {}; template::value>> struct S {}; int main(int argc, const char * argv[]) { S a; S b; }

Ответ 2



Один из способов - это просто объявить, например, typedef для базового класса шаблонного параметра внутри шаблонного класса. Если шаблонный параметр не имеет такого базового класса, то будет выдано сообщение об ошибке. Например, #include class M { virtual void f() const = 0; }; class A : public M { virtual void f() const { std::cout << "A" << std::endl; } }; class B { }; template class S { typedef typename T::M Base; }; int main() { S
s1; // S s2; return 0; } Если раскомментировать в этой программе закомментированное предложение, то компилятор выдаст сообщение об ошибке.

Ответ 3



А если у вас старый компилятор, то можно пойти длинным путем: class M { virtual void f() const = 0; }; class A : public M { virtual void f() const { std::cout << "A" << std::endl; } }; class B { }; template class IsDerivedFrom { class No {}; class Yes { No no[2]; }; static Yes Test(B*); static No Test(...); public: enum { Is = sizeof(Test(static_cast(0))) == sizeof(Yes) }; }; template ::Is> class S { }; template class S { S() { char * p = (int*)0; } }; int main() { S
s1; S s2; return 0; } Зато работает на старых компиляторах, которые не знают C++ 11.

Ответ 4



Можно использовать std::is_base_of: #include #include class M { public: virtual void f() = 0; }; class A: public M { public: virtual void f() { std::cout << "class a\n"; } }; class B { public: virtual void f() { std::cout << "class b\n"; } }; template::value>::type> class S { public: T t; void f() { this->t.f(); } }; int main() { S
a; //S b; return 0; } Если раскомментировать S b, то выдастся ошибка при компиляции.

странный синтаксис

#cpp #массивы #указатели #cpp11


очень неясно как работает данный код

int a[] = {4, 5, 2, 3, 1};
int i = *a;

for (;i;)
{
    std::cout << i--[a];  // не понятная строчка
}

//  output: 1325


к чему тут декремент применяется??? 

ну, очевидно, что к переменной i, раз цикл завершается.

Но как тогда происходят обращения к элементам массива ? почему нет ошибки компиляции ?

Впервый раз в жизни встречаю подобный код
    


Ответы

Ответ 1



Декремент тут применяется i хотя бы просто потому, что больше он в данной записи ни к чему применяться не может - справа от него стоит лексема [, которая началом выражения-операнда быть никак не может. Тема же эквивалентности a[i] и i[a] заезжена донельзя. Выражение x[y] по определению является лишь сокращенной формой записи для *(x + y). Один операнд должен быть массивом/указателем, а другой - целочисленным или enum значением. Причем ограничений на порядок указания операндов не накладывается. Поэтому ваше i--[a] эквивалентно *(i-- + a) эквивалентно *(a + i--) и эквивалентно a[i--]. Никакой "ошибки компиляции" тут нет. Вышесказанное относится именно и только к встроенному оператору []. Для перегруженного оператора [] порядок аргументов фиксирован, то есть для выражения x[y] будет рассматриваться только перегруженный вариант x.operator[](y), но не y.operator[](x).

Ответ 2



Оператор индексирования относится к классу постфиксных операторов. Он определяется следующим образом (стандарт C++, раздел 5.2.1 Subscripting) 1 A postfix expression followed by an expression in square brackets is a postfix expression. То есть за постфиксным выражением следует выражение в квадратных скобках. В этом выражении i--[a] i-- - это оператор пост-декремента, который относится к классу постфиксных выражений. Итак, имеется i-- [a] | | постфиксное выражение выражение в квадратных скобках Так что с точки зрения синтаксиса данное выражение совершенно корректное. Далее в той же самой цитате из стандарта имеется продолжение 1 ... One of the expressions shall have the type “array of T” or “pointer to T” and the other shall have unscoped enumeration or integral type. The result is of type “T.” The type “T” shall be a completely-defined object type.64 The expression E1[E2] is identical (by definition) to *((E1)+(E2)) из которого следует, что одно из выражений должно быть либо массивом, либо указателем, а другое выражение - перечислением или целочисленным типом. Какое из этих двух выражений какой должен иметь тип не существенно, так как по определению выражение E1[E2] эквивалентно выражению *((E1) + (E2)). В С++ вы можете написать даже более вычурную конструкцию, добавив знак плюс перед именем массива. Например, #include int main() { int a[] = { 0, 2, 4, 6, 8 }; int i = 0; std::cout << i++[+a] << std::endl; return 0; } В C такая конструкция i++[+a] не будет компилироваться, так как в C в отличии от C++ нельзя применять унарный плюс к имени массива. В C++ пользователь также может перегружать оператор индексирования для списка инициализации в фигурных скобках. Например, #include #include #include int main() { struct A { long long int operator []( std::initializer_list l ) const { return std::accumulate( l.begin(), l.end(), 0ll ); } }; std::cout << A()[{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }] << std::endl; return 0; } Вывод программы на консоль 55

Как начать делать текстовый квест?

#c_sharp


Нужно реализовать класс Quest в котором будут хранятся шаги.

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


Ответы

Ответ 1



В общем, @VladD абсолютно прав, вам нужен конечный автомат (в английской терминологии state machine), и желательно разобраться что это такое подробнее, но я попробую показать простой для понимания пример не используя пока специфическую терминологию. Вам попадались книги-игры Дмитрия Браславского? В прочем если бы попадались, вопрос бы не возник. И так, что такое текстовый квест? Это набор параграфов, описывающих некоторые игровые сцены в которых игроку требуется принять некоторое решение, чтобы перейти к другой сцене. Ок, от этого и пойдем. Пронумеруем все параграфы в произвольном порядке. Для книги нумерация имеет значение, для компьютерного варианта - нет. Размещаем параграфы в массив согласно сделанной нумерации. Каждый выбор игрока, фактически, означает выбор следующего параграфа, но выбор в слепую, т.к. игрок не знает содержимое параграфа (во всяком случае в первой игре). Таким образом, необходимо сопоставить предлагаемые игроку действия на текущей сцене с номерами параграфов на который они ведут. Когда игрок сделал свой выбор, мы просто показываем ему текст следующего параграфа с соответствующим индексом в массиве и ожидаем следующего выбора игрока, разумеется если это выбор у него есть в данной сцене. Естественно возможен вариант, когда игрок может попасть на одну и туже сцену (параграф) разными путями. Таким образом, вам достаточно одного единственного класса для описания модели вашего квеста, при этом ни чего мудреного не требуется. Для консольного варианта достаточно базовых знаний C#: синтаксис, условия, массивы, циклы. ну и немного стандартных классов для организации ввода/вывода. В эту модель прекрасно вписывается и "боевка" с бросанием кубиков, и применение ранее приобретенных вещей, если сцена предполагает возможность их применения. Какую к данной модели приделать "шкурку": консоль, или развесистый графический интерфейс с иллюстрациями и музыкой, не имеет значения, т.к. самое сложное в данном типе игр - создать сюжет и написать тексты игровых сцен, чтобы они не были все на одно лицо. Единственное что я бы посоветовал лично от себя - не показывайте игроку настоящие номера параграфов, спрячьте их за кнопками с текстом, локальными номерами (1,2,3 и т.д. по количеству вариантов выбора в сцене) для консоли, ссылками для Web-версии. Так интереснее, и больше вероятность, что игрок хотя бы прочитает (а может и запомнит) текст над которым вы или ваш сценарист трудились, создавая квест. Ну и немного кода для иллюстрации возможной (но не единственной) реализации такой модели: Простейший вариант для консоли с одним классом: class Quest { public string[] Paragraphs {get; set;} public int[][] Choises {get; set;} //Для консольного варианта вы просто отображаете номера для выбора прямо в тексте //параграфа, рядом с описанием выбора, как в книжном варианте. } Более сложный пример: class Paragraph //параграф (игровая сцена) { public int Num {get; set;}//не особо нужен, но для полной картины пусть будет public string Text {get; set;} //доступные варианты, список может быть нулевой длины, но не null. public List Choises {get;} = new List(); } class Choise { public int NextParagraph {get; set;} public string Text {get; set;} //текст варианта выбора для игрока } class Quest { Paragraph Current {get; set;} //Если номера параграфов соответствуют индексам можно так List {get;} = new List(); //Если лень следить за соответствием - так Dictionary {get;} = new Dictionary(); } Если знаете что такое направленный граф, то можно еще проще class Paragraph //параграф (игровая сцена) { public string Text {get; set;} //текст параграфа //доступные варианты, список может быть нулевой длины, но не null. public List Choises {get;} = new List(); } class Choise { public Paragraph Next {get; set;} public string Text {get; set;} //текст варианта выбора для игрока } class Quest { Paragraph Current {get; set;} Paragraph StartPoint {get; set;}; } С кодом ввода выбора игрока и вывода текста на экран, думаю сами справитесь.

непонятное поведение аргумента функции

#cpp


когда вызываю функцию прямо в ostream, не получаю нужный результат, а когда вызываю
до cout, то все нормально...

#include 
#include 
using std::string;

string pop_word(const char*& p)
{
    const char* q = p;
    size_t count = strlen(q);
    string s = p;
    while (!isdigit(*q) && --count > 0) ++q;
    string str = s.substr(0, q - p);
    while (*q == ' ' && --count > 0) ++q;
    if (*q) p = q;
    return str;
}

int main()
{     
    const char* p = "unsigned  45678";
    const char* q = p;

    string s = pop_word(p);
    std::cout << s  << '\n' << p << '\n';
    // p указывает на "45678" (все нормально)

    std::cout <


Ответы

Ответ 1



Наблюдаемое вами поведение вызвано тем, что ваш компилятор не удовлетворяет требованиям С++17 или работает в до-C++17 режиме. До С++17 порядок вычисления аргументов в выражении std::cout << pop_word(q) << '\n' << q; был не специфицирован. Компилятор имел право сначала вычислить (подготовить, передать) аргумент q, а затем вычислить аргумент pop_word(q). А мог поступить и наоборот. Так как pop_word(q) изменяет значение q, результат отличался бы в этих случаях. Но начиная с С++17 левый операнд <<, как для встроенного, так и для перегруженного оператора, должен вычисляться до того, как вычисляется правый (спасибо @wololo за подсказку). Это означает, что в данном выражении вывод однозначен unsigned 45678 P.S. Что интересно, GCC в режиме -std=c++17 -Wsequence-point все равно продолжает предупреждать о неопределенном поведении в int i = 0; std::cout << ++i << ++i << ++i << std::endl; хотя в С++17 никакого неопределенного поведения тут не должно быть.

Ответ 2



В каком порядке будут вычислены операнды неизвестно. В Вашем случае, сначала запоминается значение q, и только потом выполняется pop_word. Выведите q в следующем statement'е, думаю, там всё нормально. std::cout <

Python Взаимодействие с cmd.exe

#python #windows #python_3x #cmd #subprocess


Добрый день.
С питоном знаком не так давно.
Уже не первый день бьюсь с такой задачей:
Необходимо вызвать командную стоку windows,  отправить команду (например ipconfig)
Затем полученный результат сохранить в notepad.
Пробовал через pywinauto/ subprocess, к сожалению дальше вывода cmd экран продвинуться
не смог.
Подскажите, как можно реализовать, или в каком направлении искать :-) 
Заранее спасибо.
    


Ответы

Ответ 1



Чтобы получить результат команды как строку, не обязательно cmd.exe вызывать, можно напрямую запустить дочерний процесс: import subprocess output = subprocess.check_output('ipconfig') Чтобы "сохранить в notepad", просто в файл пишите результат: from pathlib import Path Path('~/output.txt').expanduser().write_bytes(output) Вместо приведённого пути (сохраняет файл output.txt в домашнюю директорию пользователя), можно специфичную для программы директорию использовать (текущую рабочую директорию или путь относительно скрипта или что appdirs модуль возвращает). Можно за один шаг: вызывать команду и сохранить её результат в файл сразу: with Path('~/output.txt').expanduser().open('wb', 0) as file: subprocess.run('ipconfig', stdout=file, check=True) См. Python - Запуск cmd.exe с аргументом, и последующим сохранением ответа в .txt файл Чтобы сохранить вывод, используя другую кодировку (к примеру, если вы кракозябры видите), необходимо декодировать вывод программы в Unicode, а затем сохранить в кодировке, которую понимает ваше окружение: import ctypes import os encoding = os.device_encoding(1) or ctypes.windll.kernel32.GetOEMCP() text = subprocess.check_output('ipconfig', encoding=encoding) Byte при печати вывода внешней команды. После этого в желаемой кодировке можно сохранить. К примеру, по умолчанию используется locale.getpreferredencoding(False) кодировка: Path('~/output.txt').expanduser().write_text(text) вы можете в write_text() другую кодировку передать (к примеру, encoding='utf-8').

Ответ 2



os.system("ipconfig >some-file.txt")

Ответ 3



Большое спасибо за помощь. Получившийся работающий код: def ipconf_cmd(): text = subprocess.check_output('ipconfig') decoded = text.decode('cp866') Path('~/output.txt').expanduser().write_text(decoded)