All streams
Search
Write a publication
Pull to refresh
38
0.1
Николай Меркин @nickolaym

User

Send message

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

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

(lambda i : (lambda x : x + i))(i)

Уникальные лямбды с немедленным связыванием текущего значения можно рожать вот так:

for i in range(3):
  functions.append(
    (lambda the_i :
      (lambda x : x + the_i)
    )(i)
  )

Вот эта статья - ХАБР ТОРТ. А не бесконечные корпоративные блоги про абы что лишь бы контент наколотить.

Нужно ещё учесть динамику выливания воды, то есть, способность дозировать. Чайник, который не льёт - не льёт - не льёт - ПЛЮХ!!! - не льёт - не льёт... - плохой, как бы у него ручка ни располагалась.

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

Но даже со всем этим вашим хиндли-милнером. Посмотрите, чего понапихали в окамл и в хаскелл... Не потому что могут, а потому что нужда заставила. Функторы и тайпклассы, монады и линзы там всякие. ФП <-> ООП для бедных.

Это далеко не те же яйца.

Совершенно разные вопросы производительности.

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

Исключения не требуют проверок почти никогда. Но усложняют покрытие кода. Утяжеляют прологи функций, хотя и не намного. Зато бросок исключения - очень дорогое дело. Это и создание объекта исключения, и динамическое определение типа в месте ловли.

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

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

---

И это совершенно разные системы типов.

Optional и ему подобные явно участвует в типе результата. Что вижу, о том пою.

Исключения - это теневая система типов. Если делать её явной, это приводит к изрядному синтаксическому шуму (придётся во все функции писать аннотацию throws(такое,сякое)). Причём это будет влиять на тип функции. Причём с ковариантностью.

// любители труъ-ООП, с интерфейсами это то же самое будет

void f() throws {A,B,C};
void g() thows {C,D};

void (*pfg)() throws {A,B,C,D,E} = flag ? &f : &g;
void (*pf)() = &g; // нет!

На практике это означает, что все плюнут и станут аннотировать только throws/nothrow. Или дефолтиться к throws. И мы возвращаемся к теневой системе.

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

Виш ю хаппи дебаг.

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

Типа, "8$ / single month, no automatic prolongation", "8$ / month", "48$ / year (4$ / month)"

Ну или если это оправдано экономически и маркетологически, то "2$ / week, no prolongation". (Получится 10 баксов в месяц).

Дизайн клавиатуры мака - и минималистичность? ПЯТЬ управляющих кнопок!

Шорткаты у приложений - сплошное not invented here, всю стандартизацию (которую IBM продвигали ещё с 1990-х годов) коту под хвост.

Когда аналитиков заставляют на собесах кодить как кодеры, - эээ это какие-то странные собесы. Хотя я не аналитик, не участвовал в таких собесах ни с какой стороны :)

Но как мне кажется, аналитикам лучше бы зашла теоретическая база - хоть Чёрч с лиспом и Карри с хаскеллом, хоть Кнут с искусством пряника в трёх томах.

Больно не от элементарщины, а от того, что изучение предмета происходит наощупь.

Кстати, "для непрограммистов" - это для кого именно?

Для математиков может жестоко зайти лямбда-исчисление Чёрча.

Пусть список (представленный как рекурсия конс-пар) - это некий загадочный объект x.

Над ним можно и нужно определить функцию проверки на пустоту IsNIL(x). А над непустым списком - функции Head(x) и Tail(x), возвращающие головной элемент и хвостовой список.

А также нам понадобятся два конструктора: константа NIL и конструктор пары Cons(head,tail).

Раз у нас есть булева функция, то нам нужны константы True и False.

А над ними нужна булева алгебра.

И вот вся эта красотища прекрасно упихивается в голое лямбда-исчисление - где ничего, кроме лямбда-функций, вообще нет.

(Пардон, мне лень сейчас пересказывать, как это делается. Погуглите, если сможете. Ну или в википедии: https://ru.wikipedia.org/wiki/Кодирование_Чёрча)

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

Итак, поехали.

  • Связный список и список — это одно и то же?

  • Как им пользоваться? (Не супералгоритмы, а просто увидеть, что он хранит внутри.)

  • Почему в разговорах о связных списках всегда есть упоминание класса, который создаёт этот список?

  • Как создавать, как выводить и как вообще что-то с ними делать на уровне LeetCode Easy и на собеседованиях?

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

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

Связный список - ещё со времён лиспа - определён как рекурсивная структура данных. Это пара (голова, хвост), где хвост также является списком. В самом конце (в самой глубине) там пустой список, специальное нулевое значение. (x1, (x2, (x3, (x4, nil)))).

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

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

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

Почему в разговорах о связных списках всегда есть упоминание класса? Ну, может быть, потому, что это особенности классово-ориентированных языков. (Даже не объектно-ориентированных, а их подмножества). Ещё со времён лиспа доказано, что всё, что нам нужно - это два служебных типа - нульпоинтер (NIL) и пара (CONS). И это позволяет построить абсолютно любые древовидные структуры данных (а список - это вырожденное дерево с одной длиннющей ветвью направо). Классы не нужны. Но с классами - капельку удобнее.

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

Как создавать списки. Тут, как говорится, два путя.

Если список неизменяемый, то новый список легче всего создавать, добавляя новую голову: new_head + old_list = (new_head, old_list).

Если можем себе позволить изменять узлы, то

1) получаем указатель на последний узел - пару (последний элемент, NIL)

2) создаём новый узел (совсем последний элемент, NIL)

3) у последнего узла изменяем указатель "хвост" - переставляем его на этот новый узел

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

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

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

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

Как перебирать элементы списка. Элементарно, ватсон. Взяли указатель на головную пару. Взяли левый компонент этой пары и что-то с ним сделали. Переставили указатель на правый компонент этой пары - то есть, на следующую пару. И так далее до NIL. Можно рекурсивно, можно в цикле.

Как узнать длину списка. Пробежать до конца и посчитать.

Как отличить, занимается ИИ дедукцией, или подводит базис под тезис?

Тезис номер 1: У нас есть числа 60 и 5, 60/5 = 12, выглядит как ответ.

Тезис номер 2: первый очевидный ответ неверный. У нас есть движение туда и обратно, кажется, есть число 2, 12/2 = 6, выглядит как ответ.

А если подкинуть промпт "предположи, что и второй ответ неверный, и подумай как следует", тогда что нейронка выдаст? Упрётся рогом и скажет - нифига, 6 и только 6, или же раз деление не прокатило, давайте попробуем умножить, 12*2 = 24?

Какие-то сплошные банальности для новичков. Делай красиво, не делай некрасиво.

Я-то думал, "чистый код" - это про иммутабельность и всё такое...

А тут - ну блин, даже тема генераторов как следует не раскрыта.

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

это longjmp/setjmp.

goto просто выскакивает из алгоритма

Это нужно в хаб "ненормальное программирование"! :)

Не стал вчитываться в решение, но заценил размах замысла. Красотища.

Лишь бы москалям не досталось! Глаз себе выколю, пусть у тёщи будет зять кривой.

В 2024 году именно что нажать на кнопочку сложно. Это надо пойти в аппстор, ввести там искомую строку (которую до того надо где-то как-то узнать), выбрать среди сопадений именно то, что тебе надо, и вот только тогда нажать на кнопочку один раз, а потом второй раз, чтобы выложить на главный экран лончера. Количество кликов посчитайте сами...

А если у тебя прямо на главном экране уже кнопочки "БРАУЗЕР", "ПОЧТА", "СОЦСЕТЬ", "ИГРУХИ" то жизнь резко упрощается. Хотя и происходит вендор лок.

Думаете, как гугл подсаживает новых людей на гмейл? Ровно так, что гмейл доступен из коробки. А не то, что кто-то там делает осознанный выбор между гмейлом, мейлру и прости господи рамблером.

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

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

А тут даже не предлагается в эту игру играть вдвоём и выгонять гугловские сервисы и гугловский аппстор. А только создавать весомую альтернативу.

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

Вы тоже можете себе минимальный дебиан или центос поставить, и потом начать докачивать дрова, иксы, десктопный менеджер, компилятор, браузер, офис... Или в консольном конфигураторе галочки расставлять или буковки Y и N нажимать. А можете тупо выбрать дистрибутив с метапакетом. Вот что вам Шаттлворт или Юинг рекомендовали, то и схаваете поначалу, потом будете разбираться.

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

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

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

Information

Rating
3,900-th
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity