Программирование: введение в профессию. Издание второе, в трёх томах.

1st volume cover 2nd volume cover 3rd volume cover

Аннотация

Учебник «Программирование: введение в профессию» ориентирован на самостоятельное изучение и предполагает использование систем семейства Unix (в т.ч. Linux) в роли сквозной среды для обучения.

Публикация в бумажном варианте

Первое издание публиковалось издательством МАКС Пресс (Москва) с 2016 по 2020 г.

Второе издание опубликовано издательством МАКС Пресс в 2021 году.

  • ISBN 978-5-317-06573-7 (книга в целом)
  • ISBN 978-5-317-06574-4 (том I)
  • ISBN 978-5-317-06575-1 (том II)
  • ISBN 978-5-317-06576-8 (том III)

Электронная версия

Электронные версии каждого из трёх томов, идентичные печатному изданию, доступны по следующим адресам:

Статус бумажной версии

Имеется в продаже.

Архив примеров программ

Архив, содержащий примеры программ, можно скачать здесь: progintro_e2_examples.tgz. Напоминаем, что раскрыть этот архив можно командой
   tar -xzf progintro_e2_examples.tgz

Файл stud_io.inc, используемый в начальных примерах третьей («ассемблерной») части, можно взять здесь: http://www.stolyarov.info/books/extra/stud_io_inc. Не забудьте переименовать файл! Это делается так:

  mv stud_io_inc stud_io.inc

Errata

Список найденных в тексте ошибок и опечаток находится здесь.

Том 1, страница

Том 1, страница 191
> Рассмотрим теперь функцию g(n)=f_n(n) + 1. Ясно, что в любом разумном смысле такая функция вычислима

А вот не ясно. То есть, +1-то конечно вычислимо, но мы тут используем ещё одну операцию — индексацию, то есть выбор функции из множества вычислимых функций по её номеру.

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

Если же мы возьмём дырявую индексацию, которая точно вычислима (например каждый алгоритм представим в виде 256-ричного числа, соответствующего байтам его записи в UTF-8), то далеко не всякому натуральному числу будет соответствовать осмысленный алгоритм, поскольку мы нумеруем все возможные последовательности байт, соответственно для многих n, посчитать g(n) будет невозможно, поскольку мы как правило не сможем скомпилировать какие-то случайные байты.

То есть, даже если все f_n вычислимы и их множество счётное, g() не вычислимо, и не входит в последовательность f_n. То есть не вижу никакого противоречия.

admin аватар

Никаких

Никаких "дырявых индексаций". Я вообще в этой области не бог весть какой спец, но множество алгоритмов относится не просто к счётным, а к рекурсивно перечислимым, что заведомо означает разрешимость задачи о вычислении номера заданного алгоритма — в смысле, существует такое ВЗАИМНО ОДНОЗНАЧНОЕ (!) соответствие между натуральными числами и алгоритмами, что по любому алгоритму можно установить его номер. Например, тупо запустив ту "рекурсивную перечислялку", из-за которой алгоритмы рекурсивно перечислимы, и дождаться, пока она выплюнет нужный алгоритм, одновременно пересчитав все, которые она успеет выплюнуть до него. Ждать, конечно, придётся долго, но в ЭТОЙ части теории "долго ждать" никого не смущает.

Про перечислимые множества, например, тут:

https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D1%87%D0%B8%D1%81...

UPD: пардон, вы про функции, я про алгоритмы — но сути это не меняет, вычислимые функции тоже рекурсивно перечислимы.

Вычислимые функции

> но сути это не меняет, вычислимые функции тоже рекурсивно перечислимы.

Вычислимые функции в определении (которое вы пытаетесь опровергнуть) должны быть определены на любом аргументе принадлежащем N.

А вот насчёт того что можно перечислить все такие функцию по порядку — вот тут мне кажется, что не выйдет. Множество-то их счётное, но как вы их проиндексируете? Именно функции, определенные на всём множестве N и при этом вычислимые.

Пример функции которую будет трудно проиндексировать — функция first_twin(n), возвращающая первое число из n-ой пары простых чисел близнецов. До сих пор математики не знают, конечно количество близнецов или бесконечно, значит мы не знаем, входит ли эта функция в нашу последовательность {f_n}. Например, модифицированное решето Эратосфена, которое отсеивает не-близнецов может на каком-то n зависнуть, если окажется, что близнецов больше нет.

А если уж мы даже про конкретную функцию не знаем, входит ли она в {f_n}, то как мы можем быть уверены в вычислимости индекса какой-то следующей функции среди {f_n}? Если перечислялка столкнётся с first_twin(), поместит она её в {f_n} или пропустит? В зависимости от этого сдвинутся индексы всех прочих функций выше. Или перечислялка вообще не будет рассматривать first_twin()?

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

То есть, ваша g() действительно невычислима, и +1 тут непричём.

admin аватар

Вообще, ещё раз

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

Кратко говоря, что такое вычислимые функции — неизвестно, но есть тезис Чёрча, предполагающий, что класс вычислимых совпадает с классом частично рекурсивных, а эти последние строятся через суперпозицию нескольких крайне простых операций и операторов (операторы в данном случае из одних функций делают другие). Поскольку базовых операций там меньше чем пальцев на руках (впрочем, это пофигу, достаточно того факта, что их конечное число), очевидным образом можно выстроить дерево всех возможных Ч/Р-функций и в этом дереве ввести некую нумерацию, а дальше запустить алгоритм, который это дерево будет обходить и, зайдя в очередную вершину, выплёвывать соответствующую формулу. Это ваше "индексирование" на базе такого алгоритма строится очевидным образом: функцию по номеру получаем, запустив алгоритм и проигнорировав первые N функций, а обратную операцию -- номер по функции (!) -- получаем, запустив алгоритм и дождавшись, пока он выплюнет нужную функцию. Ещё раз: да, практического смысла эти построения не имеют, поскольку для любой мало-мальски интересной с практической точки зрения функции ждать придётся, вероятно, не один миллион лет, но здесь (в теории вычислимости) такие мелочи никого не беспокоят. Так вот, точно так же будет обстоять дело и с гипотетической последовательностью всюду определённых функций, которые хочется "подставить" вместо Ч/Р в роли модели вычислимой функции, чтобы не было всей этой "фигни" с частичной неопределённостью: если мы что-то конструктивно делаем, то у нас есть вычислитель, на котором мы это делаем, а у него -- базовый набор операций, далее см. выше.

Кстати, чего я с ходу не знаю — так это как соотносится класс примитивно-рекурсивных функций к классом функций всюду определённых. Это к специалистам.

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

По ссылке или значению?

Здравствуйте, читая 145 страницу третьего тома (о переопределении операции присваивания), наткнулся на примеры для класса Complex.

Здесь в параметрах метода вы используете константную ссылку для операнда:


  1 class Complex {
  2     // ...
  3     const Complex& operator=(const Complex& c) 
  4         { re = c.re; im = c.im; return *this; }
  5     const Complex& operator+=(const Complex& c)
  6         { re += c.re; im += c.im; return *this; }
  7     // ...
  8 } 

На этой же странице, ниже, вы уточняете, что то же (переопределение) можно сделать и с операциями для других типов:


  1 class Complex {
  2     // ...
  3     void operator=(double x) { re = x; im = 0; }
  4     // ...
  5 }   

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

admin аватар

Здесь стоит

Здесь стоит применить здравый смысл. Если примерно прикинуть, как всё это работает на уровне машинных команд (а именно для этого в первом томе имеется часть про программирование на языке ассемблера) и не забывать, что ссылка — это вовсе не пустое место, а аж целый адрес, то можно заметить, что накладные расходы даже на копирование 16-байтной структуры (то есть всего объекта типа Complex) могут оказаться меньше, чем косвенные обращения по адресу (в данном случае по ссылке) внутри тела функции. Что касается типа double, то на современных (читай — 64-битных) машинах он и вовсе занимает столько же места, сколько адрес, так что экономии не будет вообще никакой, при этом потери на передаче по ссылке — будут. Кроме того, стоит учесть, что компилятор эти методы, естественно, заинлайнит, и прикинуть, что и как с этим сможет сделать оптимизатор; ощущение такое, что при передаче double по значению в итоговом коде вообще получится один mov, тогда как по ссылке — может, и получится, но не факт; я не проверял, просто понимаю, что такая оптимизация сложнее.

В случае

В случае инлайнинга и оптимизаций компиляторы получат одинаковый код для передачи и по ссылке, и по значению. Как ни странно, подобная оптимизация при современном устройстве компиляторов происходит "сама собой" как побочный эффект построения SSA формы и устранения промежуточных копий.

Уточнение по практике на языке assembler'a (NASM)

Читаю первый том, часть по языку assembler. На странице 656 сказано, что программы для практического применения лучше писать на языке Си. Есть ли смысл писать большие программы (такие как "Задача о ханойской башне", "Сопоставление с образцом") на NASM? Или достаточно посмотреть как работает та или иная команда и потом перейти ко второму тому?

admin аватар

Минуточку, это

Минуточку, это вы задачу о ханойских башнях называете "большой программой"?!

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

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

Применение термина "память"

Здравствуйте, Андрей Викторович,

Во втором издании на странице 281 есть сноска под номером 6. В ней обсуждается противоречивость использования термина "память" в тексте выше. Непосредственно в тексте сноски утверждается, что памятью следует называть лишь те ЗУ, с которыми ЦП может работать "через шину без применения контроллеров".

Однако, насколько мне известно, с любым ЗУ ЦП общается именно через контроллер, и device path любого диска на шине на самом деле - путь до контроллера этого устройства. Наоборот, контроллеры могут общаться без процессора, если поддерживают DMA :)

Буду рад получить Ваш ответ и огромное спасибо за книги.

admin аватар

> с любым ЗУ ЦП

> противоречивость

там нет никакой противоречивости

> с любым ЗУ ЦП общается именно через контроллер,

Плюньте в глаза тому, кто вам сказал такую ахинею. Оперативная память, она же "оперативное запоминающее устройство", т.е. ОЗУ, по-английски RAM (random access memory) — разумеется, никаких контроллеров не требует и не может их требовать. ПЗУ, собственно говоря, тоже (процессор их вообще обычно не различает).

> device path

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

> любого диска на шине

Ещё раз, и медленно: диски — не память. Сноска, собственно говоря, об этом.

Почему я об этом вообще заговорил

Разработка аппаратуры процессора идет обычно по двум направлениям - собственно, ядро (core) и все остальное (uncore, общепринятый профессиональный сленг, если позволите). Я даже перепроверил: по запросу вроде "chip die layout of a cpu" можно посмотреть "планировку" кристалла процессора. И на этой планировке почти всегда будут ядро/ядра, система кэшей и периферийный блок.

В периферийном блоке как раз и располагаются в том числе контроллеры памяти. Поэтому меня немного и смутил текст сноски. Контроллер памяти, в том числе DDR, не перестает быть контроллером, даже если он не отдельная микросхема.

P.S.
"Противоречивость" - не совсем подходящее слово, согласен

admin аватар

Если под

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

Внутреннее устройство процессора, тем паче многоядерного, в книге не рассматривается.

Влезу, с вашего

Влезу, с вашего позволения. Всё же без контроллера вообще голое процессорное ядро может работать только со статической памятью, как в старых компьютерах вроде Спектрума или какой-нибудь Амиги. То есть вот, натурально, микросхема с памятью на статических регистрах, организованной в виде одномерного массива, индексируемого числом, заданным на шине адреса. Хотим читать или писать - дергаем один из двух стробов, в ответ на это микросхема либо считывает значение с шины данных, либо выдаёт. Шины процессора и памяти соединяются напрямую, без посредников. В динамической же памяти, как минимум, адресное пространство нелинейное, требуется выставлять на шине адреса сначала индекс банка и строки в нем, делать строку активной, затем выставлять индекс столбца, периодически обновлять содержимое, т.к. ячейки постепенно теряют заряд, при этом запрещать доступ к памяти либо переключать банк, засылать в микросхему служебные команды типа PRECHARGE или REFRESH, etc. Это уж точно не забота ядра, у него шины адреса и данных в "классическом" смысле, которые подключается именно к контроллеру, преобразующему запросы к себе как к памяти в команды оперативке и выдающему в обратную сторону данные (опять же, ядро вообще не при делах, для него это работа со статической памятью - выставили адрес, прочитали/записали). Другое дело, что это контроллер специфический и шина, на которой он сидит, за пределы кристалла не выходит. Но, конечно, если строго ограничить определение контроллера по принципу "не висит на общей шине и не плюётся в порты - не контроллер", то да, можем считать, что работа с памятью идёт напрямую.

admin аватар

Сказанного

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

1) тонкости взаимодействия процессора с памятью, как и внутреннее устройство процессора, в книге не рассматриваются вообще;

2) не следует смешивать терминологию из разных предметных областей.

У топик-стартера, если вы не заметили, в голове полнейший бардак, он к ОЗУ пытается device path применить, а вы тут про какое-то там ядро (процессора, гм...)

Сказанного

Сказанного мной всё это никак не отменяет и ему не противоречит
Я не собирался вступать в полемику - разумеется, противоречия нет. Комментарий, скорее, в догонку к ответу на вопрос топик-стартера, просто не очень люблю добавлять комментарии перед вашим, т.к. потом трудно найти начало обсуждения, если N-ое количество человек подключается к дискуссии.

Почему Tcl?

Здравствуйте.

В третьем томе есть раздел о Tcl. Я пытался найти в вашей книге объяснение, почему именно он, а не Python, Perl? Какие у него есть преимущества?

admin аватар

Python, Perl, Ruby, даже

Python, Perl, Ruby, даже Lua (хотя, возможно, в меньшей степени) — это безобразно раздутые монстры, напрочь лишённые внутренней логики, и, самое главное, сделанные без понимания границ скриптового программирования.

На текущий момент Tcl — единственный из виденных мной из командно-скриптовых языков, который не вызывает у меня жёсткого отвращения, скорее даже наоборот — он кажется мне на удивление красиво спроектированным. А ещё его автор изначально не предполагал НЕскриптового использования Tcl и был крайне удивлён тем, что кто-то стал на Tcl писать программы заметного размера.

Кстати, об этом в книжке написано — в части, посвящённой скриптингу.

shell

том 1 стр 81 конструкция с фигурными скобками не работает

 
[ "$1" = "" ] && { echo "No dir name" ; exit 1 }

работает с круглыми скобками

[ "$1" == "" ] && ( echo "No dir name" ; exit 1 )

П.С. Сравнение лучше делать двумя знаками равно ==, один знак равно это присваивание.

admin аватар

Для начала не

Для начала не надо путать скрипты на shell'е с Си и его наследниками, это совершенно разные языки; аналога сишной "операции присваивания" в скриптовых языках нет, так что знак равенства использовать можно и нужно (а двойное равенство в команде test поддерживается только для тех, у кого Си выжжен прямо на коре головного мозга).

А что не работает — ну да, там точка с запятой пропущена перед фигурной скобкой, должно быть так:

 
[ "$1" = "" ] && { echo "No dir name" ; exit 1 ; }

С круглыми, кстати, работать не будет — точнее, синтаксически оно корректно, но скрипт не завершит, поскольку круглые скобки — это subshell, вот его exit и завалит, а родитель (сам скрипт) останется.

sparse_t.cpp

Здравствуйте. В примере программы шаблона класса разреженного массива, переопределенные операторы инкремента оставлены "не шаблонными", то есть в строках 121-122 и 131-132:

int& location = Provide();
int res = ++location;

вместо "T" оставлен "int". Это сделано намеренно? Я понимаю, что для некоторых типов операция инкремента может быть не определена, но из-за этого программа не компилируется даже при использовании операции инкремента с инстанцированым типом double.

admin аватар

Вот не везёт же

Вот не везёт же этому примеру, а...

Нет, разумеется, это не было сделано намеренно, это эффект от преобразования обычного класса в шаблон. Ну и, увы, недостаточно серьёзного тестирования.

UPD: Выложил архив примеров с исправленным sparse_t.cpp. К счастью, в книжках (что в этой, что во "Введении в Си++") методы этого шаблона в тексте не воспроизводятся.

Про неизменяемые указатели

Если вам это понадобилось, свяжитесь с автором книги и расскажите, как вы дошли до такой жизни (речь о неизменяемых указателях из ¶4.3.15 второго тома)
Связываюсь, рассказываю :) В самом деле, для "больших компьютеров" с ходу предложить вариант использования такой конструкции довольно проблематично, но есть ведь еще и компьютеры поменьше — микроконтроллеры. Там имеет смысл использовать такие указатели для работы с регистрами отображенной в память периферии, они-то уж точно никуда не убегут и не сменят место жительства. Конечно, в стандартных заголовочниках адреса таких регистров чаще всего "дефайнят" в духе

#define PERIPH_REG *((volatile uint32_t *)(0xDEADBEEF))

и некоторые тащат в свой код подобные вещи. Но, например, возникает задача подключения к микроконтроллеру чего-либо с большим количеством ног - скажем, нескольких разрядов семисегментного индикатора без специализированного драйвера или внешней памяти (хотя для памяти, конечно, лучше взять специально заточенный под это дело контроллер). Хардкодить работу с индикатором, естественно, не с руки - завтра может понадобиться увеличить число разрядов или перейти с общего катода на общий анод. Разумнее использовать т.н. "виртуальные порты". Линия GPIO представляется в виде структуры с двумя полями - адресом регистра, запись в который приведет к изменению уровня на лапке МК, и собственно номера бита в регистре, за нее отвечающего. Порт - массив таких структур. Линии в ходе эксплуатации меняться не будут, поэтому логично снабдить указатели в этих структурах модификатором const и проинициализировать сразу в описании, т.е. получается конструкция в стиле (код под 32-битный контроллер, не пугайтесь)

typedef struct vport_line_td {
    volatile uint32_t * const port;
    const uint32_t line_number;
} vport_line;

typedef struct vport_td {
    vport_line * const lines;
    const uint32_t width;
} vport;

А где-то в пользовательском коде крутятся

    void vport_write(vport *p, uint32_t data);
    uint32_t vport_read(vport *p);

, "растаскивающие" или, наоборот, собирающие data по линиям порта.
Конечно, по памяти и скорости работы такой способ несколько проигрывает "захардкоженной" работе с линиями GPIO, но гораздо более гибкий: тот же семисегментник - это один восьмибитный порт на сегменты и один на общие линии разрядов; завтра нужно поменять схему его подключения - переписали инициализатор в одном месте. Если говорить об ARM Cortex-M, например (а это "сердце" подавляющего большинства современных контроллеров), то gcc-шный компилятор такие "константы" кладет во флеш-память без посторонней помощи (в отличие от AVR, кстати, но кому они сейчас впились где приходится извращаться с макросами из pgmspace.h); в оперативке они не хранятся, а пожертвовать сотней байт во флешке уже на контроллере с 8+ КБ ПЗУ ради удобства вполне можно.

Не вполне

Не вполне понятно, почему не сделать "иначе":

typedef struct vport_line_td {
    volatile uint32_t * port;
    uint32_t line_number;
} vport_line;

typedef struct vport_td {
    const vport_line * lines;
    uint32_t width;
} vport;

void vport_write(const vport *p, uint32_t data);
uint32_t vport_read(const vport *p);

Так никто,

Так никто, вроде, не запрещал, делайте "иначе" на здоровье, оба варианта вполне валидны. Другой вопрос, что адрес MMIO принципиально неизменяем, и в первом случае это его свойство явно закреплено за соответствующим типом; во втором - просто указан адрес области памяти, ответственность за неизменяемость которого где-то и когда-то возьмёт на себя какой-то код. Ну да о вкусах не спорят. Вот чего точно первому варианту не хватает, так это const vport в заголовках функций - "порты" по-любому будут описаны с const, и компилятор ругнется, что функции снимают этот модификатор. Здесь косяк, согласен, но пример был не о функциях.

Любое

Любое использование const это именно что "где-то и кто-то что-то имел в виду", а "принципиально неизменными" можно считать разве что константы в enum. Отбросив полемику, пожалуй, остаются стилевые предпочтения: лично мне описание "неизменяемой" области памяти вида const vport_line[] vport_name = { ... }; кажется более... идиоматичным?

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

admin аватар

> кажется

> кажется более... идиоматичным?

ну кому как, мне оно кажется синтаксической ошибкой

Ёлки-палки const

Ёлки-палки const vport_line vport_name[] = { ... };

admin аватар

Супер, спасибо :-)

Получил эстетическое наслаждение :-)

Может ли мое

Может ли мое непонимание большей части теории про перевод двоичный дробей в десятичные, кроме первого примера, и того, что такое область определения и область значений функции (со школы этого не помню) из части про двоичную логику повлиять на дальнейшее понимание материала и материала по программированию на Паскале? Могу ли я, если пока что мне это непонятно, пропустить вышеописанный материал, и освоить его позже, например, на практике, когда я с ним столкнусь? Или я все-таки обязан это освоить прямо сейчас?

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

Область

Область определения функции — множество значений, которые может принимать её аргумент (аргументы), область значений функции — множество значений, которые может принимать результат функции. Вроде просто же.

admin аватар

В такой

В такой формулировке это "просто" только для тех, кто знает, о чём идёт речь.

admin аватар

Может-может.

Может-может. Увы.

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

Редактор

Здравствуйте, Я начал постигать emacs и уже более менее в нем ориентироваться, а потом начал читать вас и думаю, есть ли смысл пересаживаться на vim? Либо же вообще spacevim, у меня знакомый говорит, что выучи горячие клавиши емакса и потом можешь их в любую ide перенести и горя не знать

admin аватар

На вкус и цвет...

... все фломастеры разные.

Вот IDE лучше вообще не применять, ибо гадость. А vim или emacs — вопрос скорее религиозный :-)

Есть только один момент. Нужно уметь хотя бы базовые вещи делать в классическом vi (не в vim'е). Рано или поздно вы столкнётесь с ситуацией удалённого доступа к системе, в которой ничего другого не окажется, и лучше, если это не приведёт к полной беспомощности.

Вообще, в vi

Вообще, в vi который в busybox достаточно помнить в начале нажать i, а в конце Esc, :wq и всё. А в режиме редактирования работают стрелки и бекспейс, так что всё точно так же как в nano или notepad-е.

admin аватар

реализации разные бывают

В этой версии так, да. А в классическом vi, например, удалить backspace'ом можно только то, что введено в ходе последней операции ввода (то есть после нажатия того самого i, или a, или o, или O).

Том1 старой ревизии

Здравствуйте Андрей Викторович, я где то в середине первого тома уже нахожусь, по сути почти в самом начале практики, недалеко ушел, занимаюсь в свободное время после дел и работы, пытаюсь перейти в линукс, очень больно конечно иногда, особенно при установке свежих версий пакетов некомпилированных версий программ в деб пакеты и без баш скрипта инсталл и тд, прям вызывает боль и ступор иногда, с пакетом fpc 3.2.0 с оф сайта например были трудности, в репозитории убунту была версия но только старая, а у новой не поймешь толи установилась, толи нет, но компиляцию из командной строки не делала, может не туда установилась куда нужно было, потом что то нагуглил и установил из фтп какого то командой и ручной простановкой папок и пути вроде, короче пока очень больно в линукс. Учу команды сд рм пердж лс судо эко и тд, распечатал листов чит шитов с командами чтобы иногда поглядывать, чуток еле еле узнал про настройки vimrc и выбрал прекрасную тему с гитхаба, добавил нумерацию строк и тд. Убунту выбрал англ версию естественно, может поэтому мне чуть чуть сложнее чем на русской, технический англ где то около 0 уровня, чисто кино и общаться могу. Все это печатал слепым набором(пока в процессе изучения тоже, правый мизинец плачет иногда), относительно медленно правда, но я стараюсь, почему то у меня на ноутбуке не работает в убунту прямая черта которая у вас в примере была про ввод и вывод данных когда перенаправляли ввод на текстовый файл кажется, у меня она чуток наклонена почему то вправо, а так | в виндовс красивая прямая черта, в линукс как с наклоном, так и не исправил эту уродину. Еще пока что не очень верится вам про супер дружелюбность линукс систем и удобство =)) где то в ютуб прочитал фразу - Линукс это очень дружелюбная юзер-френдли система, просто она очень тщательно выбирает себе друзей, думаю эту фразу можно было бы добавить в вашу книгу, смеялся аж с нее. Ну это вводные данные были, да и просто поделиться с другими читателями вашего сайта, теперь
begin
Заметил что вы новые издания книг выпустили, а я дурак скачал старую еще до выпуска новых или не заметил в тот раз, даже не знаю теперь, хочу спросить совет что делать, ну и про отличие первой версии тома 1 от переиздания, я читал давно о ваших планах про переиздание мол вы совершили ошибку создавая сишность мозга что то такое, но по идее язык си же во втором томе появится, если новый том лучше, то перечитаю, просто хотелось бы узнать у автора, ну и заодно сказать спасибо вам за пинок в сторону линукс(хоть я пока и не согласен с тем что он дофига удобный), все таки учитывая то, что все сервера на нем, знать его на базовом уровне все таки обязательно. Ну и отдельное спасибо за референсы в книге !!! Очень юзер френдли кстати, например если вы тут не поняли то шуруйте в параграф № x.y. и перечитайте. Ну и если я закончу первый том 1 издания, то переходить на второй том 1 издания или уже второго свежего издания, не потеряю ли связь?

Терпение, только терпение!

Я тоже в свое время переполз на Линукс с Виндовс. По своему опыту могу сказать, что Линукс (лично для меня) - в разы проще чем Виндовс. И дружелюбнее.

Для чего Вам понадобилось искать новую версию FPC, при изучении книги уважаемого автора сайта? Были проблемы с компиляцией программ, использую старую версию?

По поводу команд в Линукс, название каждой команды (она же - утилита Линукс) расшифровывается. Ну, например, cd - change directory, cp - copy и.т.д. Мне, на первых порах, это помогало их проще запомнить. Да и сегодня помогает! А еще есть такая утитила apropos: советую освоить ее, и тогда проблем с командами в Линукс у Вас будет меньше.

А по поводу вот этой | черты, у меня на Линукс с ней никогда не было проблем. Я устанавливал и пробовал зарличные Линукс дистртибутивы, но вот этой проблемы не имел.

На последок могу посоветовать пару книг по Линукс. Для фундаментов мне не плохой кажется книга Пола Коббаута "Фундаментальные основы Linux": она доступна бесплатно и на английском, и на русском языках. Кроме нее, если вы используете систему с пакетами формата .deb, также бесплатно доступна книга "Настольная книга администаратора Debian": ее перевод правда выполнен не полностью, но тем не менее читать можно.

admin аватар

Бросьте нафиг

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

А что до линукса и "user friendly", так не, Linux не дружит с пользователем. Linux подчиняется пользователю. И проявляет практически 100% лояльность к пользователю. Ну, конкретно для случая Убунты, может, не 100%, но близко.

А то, как винда с пользователем "дружит" — видали мы таких друзей, ага...

Иногда бывает,

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

admin аватар

Вроде ничего не

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

Пробелы в конце строк в stud_io_inc

В stud_io_inc, доступном по ссылке на этой странице, присутствуют пробельные символы в конце некоторых строк

$ curl -s http://www.stolyarov.info/books/extra/stud_io_inc | grep -c '\s$'
4

admin аватар

Спасибо

Вот чёрт, в архиве примеров поправил, а про эту копию забыл. Исправлено.

хочу учиться

Здравствуйте. Я полный ноль в этом деле, но мне безумно интересна эта сфера и хочу начать обучаться. Эти книги предназначены для таких людей как я? И был бы рад какому нибудь совету, для того кто делает первые шаги в эту сферу, от человека который проделал огромный путь.

admin аватар

Да, именно для

Да, именно для таких и предназначены.

Что до советов, то их, пожалуй, дам два: (1) прочитайте в первом томе "напутственное" предисловие (стр.39--43) и (2) установите Linux и переползите на него со всеми делами, которые вы делаете с помощью компьютера.

А 39 летнему с

А 39 летнему с нуля есть ли смысл их прочитать? Хотелось бы переучится на программиста.

admin аватар

Будет тяжело.

Будет тяжело. Но может и получиться, почему нет.

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

Почитаю Ваши

Почитаю Ваши книги,хоте меня отговаривали,время зря в свои года теряешь.Лучше,мол переходить на C++ с нуля,есть хорошие учебники.Но мое мнение,полезно и эти книги почитать,потом дополнительно поизучать язык C и потом переходить на C++ изучать,ну не один год уйдет,за то интересно для меня.Не знаю появился интерес в 40 лет к этой области,каждый день думаю об этом.Пыталмя сразу начинать с C++,но наверное не верно,надо именно с начинать с Ваших учебников.Образования у меня нет в этой области,а интерес есть.

admin аватар

Вот если

Вот если пытаться начать с Си++, то точно ничего не выйдет, это я вам гарантирую. Причём в любом возрасте.

"Дополнительно" ничего изучать не надо, Си рассматривается во втором томе, Си++ — в третьем. Начинать ни с того, ни с другого нельзя категорически. И, самое главное, если эти или любые другие книги по программированию просто "читать", то время будет потрачено впустую. К Си можно притрагиваться не раньше, чем у ваших программ на Паскале появятся сторонние пользователи, а к Си++ — не раньше, чем объёмы ваших программ (в смысле, каждой отдельно) не выкатятся за пару тысяч строк.

А что делать тем, у кого уже есть опыт программирования?

> К Си можно притрагиваться не раньше, чем у ваших программ на Паскале появятся сторонние пользователи

В моём случае не получится. Я уже успел пописать и на паскале в школе, на C в институте, хотя там был странный гибрид с С++, причём из последнего только cout, cin и строчные комменты. Там же был курс по ассемблеру, только почему-то преподавался он на фигне, встроенной в трупный паскаль. То есть чтение данных с клавиатуры и вывод были на паскале, а алгоритм их обработки на асме.

А вот с последнего мой переход на линукс и начался. Но у меня это всё оставляло чувство неправильности. Если уж программа на ассемблере, то она вся должна быть на нём. После пары работающих hello world-ов на DOS мне захотелось написать что-нибудь на уровне голого железа, для чего нужно было после каждой пересборки записать нулевой и несколько следующих секторов на дискету, а потом перезагрузиться. Программы для этого под Windows я не нашел, но, так как интересовался другими системами, из купленной мной криво переведённой книги "юникс для чайников", я знал что там есть dd для записи произвольных секторов диска.

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

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

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

В общем, я уже не могу начать с паскаля и не трогать C, потому что уже трогал.

admin аватар

Другие

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

нет.

один преподаватель даже заплатил мне деньги за написание программы для его диссертации. Но программа там тоже уровня хелловорда.

это тоже не то.

В общем, я уже не могу начать с паскаля и не трогать C, потому что уже трогал.

И что? :-) Паскаль-то вы трогали раньше, чем Си, и определённое эстетическое чутьё у вас есть, вон вкрапления ассемблера в Паскаль вам не понравились (и это совершенно правильно, ибо нефиг).

Читать мои книжки или не читать — подозреваю, вы разберётесь сами. Только один момент: лучше всё-таки начните с начала, а не сразу с середины второго тома, как некоторые пытаются сделать, и пытаются, замечу, совершенно зря.

Так что

Так что получается,уже не стоит даже начинать.

admin аватар

Почему не

Почему не стоит-то? Что жизнь после сорока есть — это вы уже и сами на своём опыте знаете, а я вам ещё могу сказать (на моём опыте), что она есть даже после сорока шести :-)

Спасибо

Спасибо большое за ответы,я просто начал раньше читать Вашу книгу 1-й том,установил Линукс.Отговорили,говорили чтобы быстрее в эту область войти,мол не нужно тебе это,сразу изучай C++.Послушал и закинул,в итоге не тут,не там,как говорят).

admin аватар

Ну да, добрые

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

В общем

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

Что делать

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

admin аватар

Ну так пишите

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

Кстати, странно, что того же вопроса не возникло после первого тома.

В задаче на

В задаче на странице 142 тома I точно ответ 340? У меня не получается этот ответ. Забавно, что с усложнённой версией у меня проблем нет.

admin аватар

стопудов (tm)

4+16+64+256 по-вашему сколько будет?

Линукс

Современный линуксовый GUI уже ни чем не уступает другим операционным системам, читая книги предполагается отказаться от GUI совсем и использовать консоль? В линксе тоже можно копировать файлы мышкой :)

admin аватар

> Современный

> Современный линуксовый GUI уже

"Уже"? Вообще-то лет двадцать как.

> ни чем не уступает другим операционным системам

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

> читая книги предполагается отказаться от GUI совсем и использовать консоль?

Несомненно. Если быть точным, не "консоль", а командную строку. Графический режим как таковой вполне полезен, ну там фотки смотреть, видосики. GUI при использовании графики совершенно не обязателен. А что касается DE (вот эти вот иконочки для изображения файлов, папочки и прочее дерьмище), то для профессионала их использование неприемлемо вообще, вот то есть категорически.

Если считаете иначе, то убеждать я вас ни в чём не буду, но книжки тогда, наверное, надо другие выбрать, от моих толку не будет.

> В линксе тоже можно копировать файлы мышкой :)

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

Кстати, вы сайтом-то не ошиблись? Может, надо куда-нибудь в другое место пойти?

А вот на

А вот на андроиде я копирую и перемещаю файлы с помощью файлового менеджера. Хотя у меня там установлен терминал в приложениях. Зря?

admin аватар

Зря вы вообще

Зря вы вообще андроидом пользуетесь, вот это точно.

А так — когда нет клавиатуры, работа с терминалом превращается в пытку. Так что тут не вполне понятно, что зря, а что не зря.

Короче, у компьютера должна быть клавиатура — и не виртуальная, а настоящая. Если её нет — такой компьютер годится только в мусорный бак.

А нафига нужна

А нафига нужна клавиатура для чтения электронных книг? Ну кроме двух боковых клавиш (по умолчанию громкости, но в книге они листают страницы).
Специальная читалка книг оказалась неудобной из-за медленного экрана. У мобильника хоть и нет такой продолжительности работы без подзарядки, зато экран гораздо контрастнее и откликается на листание страниц почти мгновенно.
При работе в режиме музыкального плеера, кнопки управления как правило вынесены на гарнитуру (play/pause, next, previous)
Но да, работа с терминалом на андроиде — мазохизм, хотя команды вроде ifconfig иногда ввожу что бы IP-адрес посмотреть.
Кстати даже клавиатура бы не особенно помогла, в андроиде сейчас нет не busybox, ни coreutils, к тому на половине команд permission denied, если не от рута.

admin аватар

Вот я и говорю,

Вот я и говорю, зря вы андроидом пользуетесь. Всем изделиям от Google место в мусорном баке.

Получается, что

Получается, что если выбросить android и iOS, остается только "кнопочная звонилка", "городской телефон" (его вроде бы правильно называть ip-телефония) и, может быть, рация, так? Других ОС на смартфонах, вроде как, нет. Да и нужны ли они (смартфоны)?

Я, честно говоря, не против такого исхода и для меня лично смартфон и планшет никогда не заменит ноутбук или ПК.

А вы, Андрей Викторович, еще пользуетесь "городским телефоном" или "звонилка" заменяет его?

admin аватар

Совершенно

Совершенно верно, в настоящее время в природе не существует таких смартфонов, которыми можно пользоваться. Вот когда появится смартфон с полностью открытой операционкой, при этом стопроцентно лояльный своему пользователю, вплоть до возможности поменять IMEI, ежели пользователь этого хочет (и плевать, что это запрещено, видите ли, стандартами GSM, суть лояльности железяки пользователю именно в том и состоит, что единственный закон для железяки — воля пользователя) — тогда можно будет на эту тему подумать.

У меня да, "звонилка", она же "бабушкофон". Даже не один :-) Городской телефон в квартире есть (ветхозаветный проводной, то есть это даже не ip-телефония), я за него по иннерции плачу абонентку, но сам по нему не звоню (идти до него далеко, лень, проще с мобилки звякнуть), на входящие звонки не отвечаю, ибо звонят на него исключительно спаммеры. Нафиг он нужен, уже не вполне понятно, но что-то мне этот номер дорог как память, всё-таки самый первый телефонный номер, который я в раннем детстве запомнил :-) Вообще-то были у меня мысли взять какую-нибудь ip-телефонную железку, чтобы можно было через asterisk устроить всякое управление через тональный набор и интегрировать это всё с имеющейся системой умного дома. Но пока дальше мыслей не продвинулось.

Пользуюсь

Пользуюсь кнопочными мобильниками всю жизнь. И пока что не собираюсь менять привычку. После долгих лет, недавно сменил телефон, купив кнопочник Nokia 2720 Flip: это - смартфон, который работает на KaiOS - свободной ОС. Правда вот не знаю пока что о том, какие возможности у пользователя в ней менять тот же IMEI. Моэет Вас заинтересует.

>А зачем делать

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

Не всегда. Один кейс я так и не нашёл, как эффективно делать в консоли.

Есть куча фоток в директории, много дубликатов и "неудачных" - стандартная картина при копировании с фотика.

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

Там где нужно не текстовая, а визуальная фильтрация с командной строкой не всё так хорошо.

admin аватар

Ну и?

Для сортировки фоток я сам применяю konqueror, и это единственный случай, для которого я его применяю. Но моей фразе, которую вы цитируете, сие не противоречит:

зачем делать мышкой то, что можно сделать без мышки

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

GUI

Иначе я не считаю, я с вами согласен.
А как относитесь к тайловым оконным менеджерам(i3, xmonad, и т.д)

admin аватар

Тайловые

Тайловые оконники вот только что обсуждались:
http://www.stolyarov.info/guestbook#comment-3437
Кратко -- не знаю, не пробовал. Пробовать некогда :-)

Качество печати

Здравствуйте.
Меня интересует качество печати данных книг. Каково оно?
Просто, для сложных (для меня) книг, я предпочитаю их бумажное воплощение. Но крайне не приятно держать в руках и работать с книгой с плохой бумагой и полиграфией.

admin аватар

Странный вопрос, честно говоря

Мне вот что интересно, вы на какой ответ рассчитываете? В каком виде? Книги вышли в твёрдом полноцветном переплёте, корешок плоский, скрепление клеевое (да, не шитое, звиняйте), бумага, если не ошибаюсь, 65 гр/м2. Печать типографская, тип печатного станка мне не известен (но это не ризограф, если вас этот момент интересует, а вот офсет это или что-то другое -- извините, понятия не имею). Что вас может не устроить -- ну, базовый шрифт 10й, можете любую страницу из PDFок распечатать, посмотреть, подходит вам такой шрифт или нет. Больше мне ничего в голову не приходит, что можно было бы ответить, но если у вас конкретные вопросы есть, задайте их.

Здравствуйте,

Здравствуйте, очень сильно замотивирован в прочтении данных пособий, но сразу возник общий опрос по поводу того, когда лучше это собственно сделать:
1) Прямо сейчас, без выпущенного задачника (хотя бы по первому тому), придумывая себе задачки, закрепляя знания (По моему мнению, в таком случае можно опустить важные детали и как результат не понять какую-либо ключевую концепцию)
2) Дождаться полноценного задачника и после этого приступать к прочтению

Ясное дело что ответ зависит от временных рамок выпуска самого задачника. Хотелось бы узнать Ваше мнение по этому поводу )

Заранее Спасибо!

admin аватар

Вообще-то я

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

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

А как насчёт

А как насчёт https://projecteuler.net ?

Есть и другие подобные сайты, но этот первым в голову приходит.

admin аватар

Это не о том

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

Не скажу, что в практическом программировании так не бывает — бывает, конечно. Просто редко. Зато намного чаще приходится бить себя по рукам, вспоминая словосочетание "преждевременная оптимизация".

А у меня и

А у меня и брутфорс на тех задачах срабатывает. Ну подумаешь подожду 2 минуты вместо 0.02 секунд, зато потрачу на написание 20 минут вместо 2 часов.

admin аватар

Я давно туда не

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

В любом случае, если задачи из projecteuler брутфорсить, то зачем на них вообще время терять? Польза-то от них где? Между прочим, где-то в сети валяются ответы на все тамошние задачи, ну вот вбить их по одному и всё, чего там.

Брутфорс

Польза от решения брутфорсом в том, что бы потом сделать diff вывода с "правильным" решением, и узнать, действительно ли оно правильное.

admin аватар

Там нет

Там нет "правильных" решений, в смысле они там не опубликованы, а если бы и были, diff бы не помог.

projecteuler

> Там нет "правильных" решений, в смысле они там не опубликованы, а если бы и были, diff бы не помог.

Вы просто не поняли, что я имел в виду. Попробую ещё раз:

В любом случае, написать решение брутфорсом имеет смысл до того как думать над "правильным" решением. Когда "правильное" решение будет готово, можно сделать вот так:

diff <(./bruteforce) <(./good_solution)

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

admin аватар

Ответом на

Ответом на любую из задач на projecteuler является целое число, так что использование diff бессмысленно. И брутфорс тоже никакого смысла не имеет, поскольку узнать, правильный ответ или нет, можно намного проще — отправив ответ на сайт. А если очень хочется узнать правильный ответ, не решая задачу — то список ответов можно найти в поисковиках.

Поздравляю Вас,

Поздравляю Вас, Андрей Викторович! Блестящее достижение! А ведь еще говорили в анонсе 2-го издания, что через год ждать ничего не следует. Но нет, дождались :)

P.S.: Перечитывать то, что уже много раз читал в первом издании и находить новый текст, новые мысли - это удивительное удовольствие! Отдельное спасибо за это.

Спасибо

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

Cпасибо!

+ в карму и спасибо!

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступны HTML теги: <a> <em> <strong> <ins> <del> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre>
  • Строки и параграфы переносятся автоматически.

Подробнее о форматировании

CAPTCHA
Проверка на бота
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.