Русский
Русский
English
Статистика
Реклама

Библиотека

Библиотека Frontend-разработчика, часть 4 Алгоритмы

25.01.2021 10:05:20 | Автор: admin

Предисловие к статье

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

Как и любой разработчик - True Frontend'eр должен расти в своей сфере, и конечно же, уходить от простой разработки интерфейсов к чему-то более сложному.
На фоне недавно нашумевшего сериала, можно выписать такой тезис - разработка, как игра в шахматы - неинтересно играть с тем, кто ниже рангом также, как и неинтересно всю жизнь делать одно и тоже одной сложности.
Всегда хочется лезть в дебри, повышать свой уровень. И там где дебри, начинается математика, структуры данных, алгоритмы. Хотите оптимизировать загрузку - это вам к алгоритмам. Хотите увеличить скорость отображения элементов на странице - необходимо обратиться к математике и алгоритмам. Возможно сортировка пузырьком элементов была лишней...

Отдельное слово хочу сказать о структурах данных - несмотря на то, что в js полный беспорядок в плане типов - всегда есть ts, где структуры данных - это основа основ. Для этого ts и был сделан. Контролировать беспорядок. Тут то вы и вспомните о классических структурах, о которых сказано в книгах ниже

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

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

Это статья о том, что алгоритмы разработчику нужны. Это статья о том, что по алгоритмам есть интересные и нескучные книжки, которые помогут быстро понять основы и прокачать свои hard-skills.

Книга Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих - Адитья Бхаргава

Начнем с простого

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

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

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

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

К минусам могу отнести относительно поверхностное повествование об алгоритмах. Но нам глубина особо и не нужна.

Алгоритмы для начинающих: Теория и практика для разработчика - Панос Луридас

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

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

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

Раз она такая сложная - как ее читать?

Очень просто:

  1. Нашли нужную вам тему

  2. Прочли теорию

  3. Списали алгоритм

  4. Разобрали алгоритм

  5. Прочли теорию снова, потому что вы ничего не поняли

  6. Снова написали алгоритм

  7. git commit -m 'i am God' && git push origin master

  8. Вы великолепны!

Этот простой рецепт помог мне быстро разобраться со многими алгоритмами. И через несколько месяцев забыть их....

Алгоритмы - С. Дасгупта, Х. Пападимитриу, У. Вазирани

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

И все таки - зачем нужны алгоритмы?

Все предельно просто - развиваться.

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

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

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

А зачем лого Leetcode на превью?

Очень просто - где оттачивать свои навыки? Конечно же на leetcode!

Подробнее..

Перевод Fastcore недооцененная но полезная библиотека Python

21.10.2020 16:06:58 | Автор: admin

Недавно я начал оттачивать владение языком программирования Python. Я хотел изучить продвинутые паттерны, идиомы и методы программирования. Начал я с чтения книг по продвинутому Python, но информация, похоже, не откладывалась в голове без применения навыков. Хотелось иметь возможность задавать вопросы эксперту, пока учусь, а такую возможность трудно найти! Тогда ко мне и пришла идея: что, если я найду проект с открытым и достаточно продвинутым кодом и напишу документацию и тесты? Я сделал ставку, что это заставит меня изучать все очень глубоко, а поддерживающие проект люди оценит мою работу и будут готовы ответить на мои вопросы.



Предыстория


Поиском такого проекта и написанием документации с тестами к нему я занимался целый месяц и такое обучение было самым эффективным из всех, что я пробовал. Я обнаружил, что написание документации заставило меня глубоко понять не только то, что делает код, но и то, почему код работает именно так, как он работает, а также исследовать крайние случаи во время написания тестов. Самое главное, что я мог задавать вопросы, когда застрял, а люди были готовы посвятить мне дополнительное время, зная, что их разъяснения служило тому, чтобы сделать код доступнее! Оказывается, выбранная мной библиотека, fastcore одна из самых увлекательных в Python, с которыми я работал. Ее цели и задачи действительно уникальны.

fastcore это основа многих проектов fast.ai. Самое главное: fastcore расширяет Python, стремясь к устранению шаблонного кода и добавлению полезной функциональности для общих задач. В этом посте я выделю некоторые из моих любимых инструментов fastcore вместо того, чтобы делиться тем, что я узнал о языке. Моя задача вызвать интерес к этой библиотеке, и, надеюсь, мотивировать вас к ознакомлению с документацией после прочтения статьи.

Чем интересна fastcore?


  1. Ознакомление с идеями из других языков прямо в Python: Я постоянно слышу, что полезно изучать другие языки, чтобы стать лучшим программистом. Мне было трудно изучать другие языки с практической точки зрения, потому что я не мог применять их на работе. Fastcore расширяет Python, чтобы включить в него паттерны из разных языков: Julia, Ruby и Haskell. Теперь, когда я понимаю эти инструменты, у меня появилась мотивация изучать другие языки.
  2. Новый набор прагматичных инструментов: fastcore включает в себя утилиты, позволяющие писать более лаконичный выразительный код и, возможно, решать новые задачи.
  3. Изучение Python: fastcore расширяет Python, в этом процессе проявляются многие продвинутые понятия. Для мотивированных людей это прекрасный способ увидеть многое о внутренней работе языка.


Пройдемся по fastcore ураганом


Вот некоторые привлекшие мое внимание вещи, которые возможно сделать с помощью fastcore.

Делаем kwargs прозрачными


Я немного поеживаюсь каждый раз, когда вижу функцию с аргументом kwargs. Это потому, что kwargs означает обфускацию API. Мне нужно прочитать исходный код, чтобы понять, какие параметры допустимы. Посмотрим на пример ниже:

def baz(a, b=2, c =3, d=4): return a + b + cdef foo(c, a, **kwargs):    return c + baz(a, **kwargs)inspect.signature(foo)

<Signature (c, a, **kwargs)>

Без чтения исходного кода может быть трудно узнать, что foo также принимает дополнительные параметрыb и d. Это можно исправить с помощью delegates:

def baz(a, b=2, c =3, d=4): return a + b + c@delegates(baz) # this decorator will pass down keyword arguments from bazdef foo(c, a, **kwargs):    return c + baz(a, **kwargs)inspect.signature(foo)

<Signature (c, a, b=2, d=4)>

Поведение этого декоратора настраивается. Например, вы можете передать аргументы и сохранить при этом kwargs:

@delegates(baz, keep=True)def foo(c, a, **kwargs):    return c + baz(a, **kwargs)inspect.signature(foo)

<Signature (c, a, b=2, d=4, **kwargs)>

Аргументы можно исключать. Например, ниже мы исключаем из делегирования аргумент d:

def basefoo(a, b=2, c =3, d=4): pass@delegates(basefoo, but= ['d']) # exclude `d`def foo(c, a, **kwargs): passinspect.signature(foo)

<Signature (c, a, b=2)>

Возможно делегирование между классами:

class BaseFoo:    def __init__(self, e, c=2): pass@delegates()# since no argument was passsed here we delegate to the superclassclass Foo(BaseFoo):    def __init__(self, a, b=1, **kwargs): super().__init__(**kwargs)inspect.signature(Foo)

<Signature (a, b=1, c=2)>

Для получения дополнительной информации прочтите документацию о delegates.

Избегаем шаблонного кода при установке атрибутов экземпляра


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

class Test:    def __init__(self, a, b ,c):         self.a, self.b, self.c = a, b, c

Ой! Это было больно. Посмотрите на все эти повторяющиеся имена переменных. Неужели действительно нужно повторять всё это при определении класса? Уже нет! Посмотрите на store_attr:

class Test:    def __init__(self, a, b, c):         store_attr()t = Test(5,4,3)assert t.b == 4

Вы также можете исключить определенные атрибуты:

class Test:    def __init__(self, a, b, c):         store_attr(but=['c'])t = Test(5,4,3)assert t.b == 4assert not hasattr(t, 'c')

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

P.S. Вы можете подумать, что классы данных тоже позволяют избежать такого шаблонного кода. Хотя в некоторых случаях это верно, store_attr более гибок. 1

Например, store_attr не полагается на наследование, а это значит, что вы не застрянете, используя множественное наследование, когда применяете его со своими собственными классами. Кроме того, в отличие от классов данных, store_attr не требует python 3.7 или выше. Вы можете использовать store_attr в любой момент в жизненном цикле объекта и в любом месте вашего класса, чтобы настроить поведение в том, как и когда хранятся переменные. Подробности здесь.

Избегаем бойлерплейта подклассов


Одна вещь, которую я ненавижу в python связанный с подклассами шаблонный код __super__ ().__init__ (). Например:

class ParentClass:    def __init__(self): self.some_attr = 'hello'class ChildClass(ParentClass):    def __init__(self):        super().__init__()cc = ChildClass()assert cc.some_attr == 'hello' # only accessible b/c you used super

Мы можем избежать такого кода, используя метакласс PrePostInitMeta. Как? Определив новый класс под названием NewParent обертку вокруг ParentClass:

class NewParent(ParentClass, metaclass=PrePostInitMeta):    def __pre_init__(self, *args, **kwargs): super().__init__()class ChildClass(NewParent):    def __init__(self):passsc = ChildClass()assert sc.some_attr == 'hello' 

Диспетчеризация типа


Диспетчеризация типа или множественная диспетчеризация позволяет изменить поведение функции в зависимости от типов получаемых входных данных. Это характерная особенность некоторых языков программирования, таких как Julia. Вот концептуальный пример того, как работает множественная диспетчеризация в Julia. В зависимости от типов входных данных x и y возвращаются разные значения:

collide_with(x::Asteroid, y::Asteroid) = ... # deal with asteroid hitting asteroidcollide_with(x::Asteroid, y::Spaceship) = ... # deal with asteroid hitting spaceshipcollide_with(x::Spaceship, y::Asteroid) = ... # deal with spaceship hitting asteroidcollide_with(x::Spaceship, y::Spaceship) = ... # deal with spaceship hitting spaceship

Диспетчеризация типа может быть особенно полезна в Data Science, где возможно разрешить различные типы ввода (т.е. массивы numpy и фреймы данных Pandas) в обрабатывающей данные функции. Типовая диспетчеризация позволяет иметь общий API у выполняющих похожие задачи функций. К сожалению, Python не поддерживает такую функциональность из коробки. К счастью, у нас есть декоратор @typedispatch. Этот декоратор полагается на подсказки типа, чтобы маршрутизировать входные данные к правильной версии функции:

@typedispatchdef f(x:str, y:str): return f'{x}{y}'@typedispatchdef f(x:np.ndarray): return x.sum()@typedispatchdef f(x:int, y:int): return x+y

Ниже показывается диспетчеризации типа при работе для функции f:

f('Hello ', 'World!')

'Hello World!'

f(2,3)

5

f(np.array([5,5,5,5]))

20

У этой функциональности есть ограничения (также как у других способов использования этой функции) и о них вы можете прочитать здесь. В процессе изучения типовой диспетчеризации я также нашел библиотеку Python под названием multipledispatch, написанную Mathhew Rocklin создателем Dask.

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

Лучшая версия functools.partial


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

test_input = [1,2,3,4,5,6]def f(arr, val):     "Filter a list to remove any values that are less than val."    return [x for x in arr if x >= val]f(test_input, 3)

[3, 4, 5, 6]

Из этой функции вы можете создать новую функцию с помощью partial, которая устанавливает значение по умолчанию: 5:

filter5 = partial(f, val=5)filter5(test_input)

[5, 6]

Одна из проблем с partial заключается в том, что она удаляет исходную строку документации и заменяет ее общей строкой документации:

filter5.__doc__

'partial(func, *args, **keywords) - new function with partial application\n    of the given arguments and keywords.\n'


fastcore.utils.partialler исправляет это и обеспечивает сохранение строки документации таким образом, чтобы новый API был прозрачным:

filter5 = partialler(f, val=5)filter5.__doc__

'Filter a list to remove any values that are less than val.'

Композиция функций


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

def add(arr, val): return [x + val for x in arr]def arrsum(arr): return sum(arr)# See the previous section on partialleradd2 = partialler(add, val=2)transform = compose(filter5, add2, arrsum)transform([1,2,3,4,5,6])

15

Почему это полезно? Вы можете подумать, что я могу сделать то же самое вот так:

arrsum(add2(filter5([1,2,3,4,5,6])))

Все верно! Однако, композиция дает удобный интерфейс на случай, когда вы захотите сделать что-то такое:

def fit(x, transforms:list):    "fit a model after performing transformations"    x = compose(*transforms)(x)    y = [np.mean(x)] * len(x) # its a dumb model.  Don't judge me    return y# filters out elements < 5, adds 2, then predicts the meanfit(x=[1,2,3,4,5,6], transforms=[filter5, add2])

[7.5, 7.5]

Более подробную информацию о compose читайте в документации.

__repr__, но полезнее


В Python __repr__ помогает получить информацию об объекте для логирования и отладки. Ниже приведено то, что вы получите по умолчанию, когда определите новый класс. Примечание: мы используем store_attr, который обсуждался выше.

class Test:    def __init__(self, a, b=2, c=3): store_attr() # `store_attr` was discussed previouslyTest(1)

<__main__.Test at 0x7ffcd766cee0>

Мы можем использовать basic_repr, чтобы быстро получить более разумное значение по умолчанию:

class Test:    def __init__(self, a, b=2, c=3): store_attr()     __repr__ = basic_repr('a,b,c')Test(2)

Test(a=2, b=2, c=3)

Обезьяньи патчи через декоратор


Это может быть удобно для обезьяньих патчей с помощью декоратора, что особенно полезно, когда вы хотите пропатчить импортируемую внешнюю библиотеку. Мы можем использовать декоратор @patch из fastcore.foundation вместе с подсказками типа примерно так:

class MyClass(int): pass  @patchdef func(self:MyClass, a): return self+amc = MyClass(3)

Теперь в MyClass есть дополнительный метод под названием func:

mc.func(10)

13

Я еще не убедил вас? Тогда покажу вам еще один пример такого патча в следующем разделе.

pathlib.Path


Увидев эти расширения в pathlib.path, вы больше никогда не будете работать с vanilla pathlib! В pathlib добавлен ряд дополнительных методов, таких как:

  • Path.readlines: то же, что with open ('somefile', 'r') as f: f.readlines ()
  • Path.read: то же, что with open ('somefile', 'r') as f: f.read ()
  • Path.save: сохраняет файл как pickle
  • Path.load: загружает файл pickle
  • Path.ls: показывает содержимое пути в виде списка.
  • и так далее.

Подробнее об этом здесь. Вот демонстрация ls:

from fastcore.utils import *from pathlib import Pathp = Path('.')p.ls() # you don't get this with vanilla Pathlib.Path!!

(#7) [Path('2020-09-01-fastcore.ipynb'),Path('README.md'),Path('fastcore_imgs'),Path('2020-02-20-test.ipynb'),Path('.ipynb_checkpoints'),Path('2020-02-21-introducing-fastpages.ipynb'),Path('my_icons')]

Подождите! Что здесь происходит? Мы только что импортировали pathlib.Path почему мы получили новую функциональность? Потому, что импортировали модуль fastcore.utils, который патчит pathlib.Path с помощью упомянутого выше декоратора @patch. Чтобы довести дело до конца и показать, чем полезен @patch, я пойду дальше и прямо сейчас добавлю еще один метод в Path:

@patchdef fun(self:Path): return "This is fun!"p.fun()

'This is fun!'

Волшебно, правда? Вот почему я пишу об этом!

Еще более лаконичный способ написать лямбду


Self с заглавной буквы S это еще более лаконичный способ написания вызывающих методы объекта лямбд. Например, создадим лямбду для получения суммы массива Numpy:

arr=np.array([5,4,3,2,1])f = lambda a: a.sum()assert f(arr) == 15

Вы можете таким же образом использовать Self:

f = Self.sum()assert f(arr) == 15

Давайте создадим лямбду, которая будет делать группировку и возвращать максимальный элемент фрейма данных Pandas:

import pandas as pddf=pd.DataFrame({'Some Column': ['a', 'a', 'b', 'b', ],                  'Another Column': [5, 7, 50, 70]})f = Self.groupby('Some Column').mean()f(df)

Another Column
Some Column
a 6
b 60

Подробнее о Self читайте в документации.

Функции блокнота


Они просты, но удобны и позволяют узнать, выполняется ли код в блокноте Jupyter, Colab или через оболочку IPython:

from fastcore.imports import in_notebook, in_colab, in_ipythonin_notebook(), in_colab(), in_ipython()

(True, False, True)

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

Замена стандартного списка


Вы можете быть довольно счастливы со стандартным list в Python. Это одна из тех ситуаций, когда вы не знаете, что вам нужен список лучше, пока кто-то не покажет его вам. L как раз такой список с большим количеством доработок.

Лучший способ описать L сделать вид, что у list и numpy родился милый ребенок. Определите список (посмотрите на приятный __repr__, показывающий длину списка!)

L(1,2,3)

(#3) [1,2,3]

Перемешайте список:

p = L.range(20).shuffle()p

(#20) [8,7,5,12,14,16,2,15,19,6...]

Индекс [прим.перев. скорее позиция position] в списке:

p[2,4,6]

(#3) [5,14,2]

L имеет разумные умолчания, например, вот добавление элемента в список:

1 + L(2,3,4)

(#4) [1,2,3,4]

L может гораздо больше. Читайте документацию, чтобы узнать больше.

Но подождите Это еще не всё!


Есть еще кое-что, что я хотел бы показать вам, но это никак не вписываются в пост. Вот список некоторых любимых вещей, которые я не показывал в этом посте:

Утилиты


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

  • mk_class: быстро добавляет кучу атрибутов в класс
  • wrap_class: добавление новых методов в класс с помощью простого декоратора
  • groupby: похоже на groupby из Scala
  • merge: слияние словарей
  • fasttuple: кортеж на стероидах
  • Infinite Lists: полезно для увеличения размеров массивов и тестирования
  • chunked: упаковка и организация элементов

Многопроцессорная обработка


Раздел Многопроцессорная обработка расширяет соответствующую библиотеку Python, предлагая такие возможности:

  • Шкала прогресса
  • Возможность сделать паузу, чтобы смягчить состояние гонки с помощью внешних сервисов
  • Пакетная обработка для каждого воркера, если у вас нет векторизованных операций для выполнения в блоках (chunk)

Прокачивать себя в Python стало проще, ведь специально для хабравчан мы сделали промокод HABR, дающий дополнительную скидку 10% к скидке указанной на баннере.

image




Рекомендуемые статьи


Подробнее..

Google Books Ngram Viewer как инструмент для ретроспективных исследований

08.02.2021 00:09:10 | Автор: admin
Активность использования термина "transistor" c 1800 года и до наших днейАктивность использования термина "transistor" c 1800 года и до наших дней

Для принятия правильных решений в различных жизненных ситуациях очень полезно иметь адекватную модель мира. В частности, бывает полезна возможность сопоставить популярность различных предметов и оценить динамику этой популярности. Например, вы издатель, и вам предлагают перевести и издать книгу по языку программирования Fortran. Его ведь всё ещё используют, издаётся англоязычная литература, а у нас давно ничего не выходило. Или, скажем, по языку Julia. Книжек по актуальной версии языка на русском ещё нет. Можно взять какую-нибудь самую популярную английскую, перевести, издать и сорвать куш. В подобных ситуациях полезно иметь возможность подглядеть, какова популярность этих языков относительно других и какова динамика этой популярности. Пример с языками программирования приведён просто для наглядности, подобные задачи возникают и при анализе популярности различных программ, технологий, научных концепций.


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

Одним из способов составления рейтингов популярности является анализ частоты поисковых запросов. Такой способ при всей его кажущейся наивности, при разумном использовании позволяет получать довольно устойчивые оценки. На этом, например, построен рейтинг языков программирования PYPL. Рейтинг строится на основе анализа запросов для поиска мануалов по тем или иным языкам. Но никакой общепит не сравнится с авторской кухней. Иногда хочется чего-то особенного, чего в существующих рейтингах может не быть. Например, рейтинг PYPL не включает в себя Fortran. Да, этот язык явно не лидер, хотя из других рейтингов известно, что он стабильно входит в верхние 50 строчек по популярности. Не проблема. Аналогичную картинку можно получить самостоятельно, не прибегая к помощи сторонних агентств, используя инструмент Google Trends. Вот, например, если посмотреть в динамике, можно увидеть хвост популярности Fortran (синий) и для масштаба относительно стабильный, хотя и немного снижающийся спрос на Matlab (красный). У Matlab, кстати, отчётливо видны сезонные пики два раза в год. По всей видимости перед зимней и весенней сессиями:

Глядя на такие картинки, невольно обращаешь внимание на левую границу, глубже которой невозможно копнуть, а именно на 2004 год. Поскольку анализируются запросы в Google, заглянуть в более древние периоды истории с помощью этого инструмента не получится. А ведь так любопытно взглянуть на период, когда Fortran был ещё на пике популярности. Но увы, наша машина времени туда попасть не может. Она была запущена на полную мощность только в 2004 году и все более ранние периоды для неё закрыты.

Из-за отсутствия информации по каким-либо другим инструментам ретроспективного поиска, я всегда думал, что возможность заглянуть в более ранние периоды отсутствует. Но, как оказалось, такая возможность имеется, причём инструмент предлагает всё та же компания Google. Называется он Google Books Ngram Viewer. Это инструмент статистического анализа информации, содержащейся в массиве книг из Google Books. В некотором смысле это гениальное решение, ведь печатные книги существуют уже несколько столетий, и имея их полные оцифрованные тексты, вполне можно использовать их как источник статистической информации о прошлых исторических периодах. Теперь хотя бы понятно, зачем Google создал и поддерживает проект Google Books. Общая цель здесь та же, что и поиска, и у сервисов: владение информацией. Но, к счастью, с нами готовы поделиться какой-то её частью.

Что предлагается:

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

По умолчанию поиск чувствителен к регистру, то есть Fortran (написание названия для современных версий языка) и FORTRAN (написания названия для старых версий) это будут два разных слова. Можно выключить чувствительность к регистру, либо использовать арифметические выражения над введёнными поисковыми запросами, то есть написать FORTRAN+Fortran:

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

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

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

Если не придираться к деталям, а смотреть какие-то общие тенденции, то в целом всё выглядит довольно реалистично. Например, можно увидеть, как упоминание Fortran в литературе сменяется на MATLAB:

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

Например, если мы решим таким образом сопоставить популярность в литературе таких слов как Fortran+FORTRAN, MATLAB и Julia, то для первых двух это явно будет название языка программирования, а в последнем случае в первую очередь что-то совсем другое, включая различные имена собственные:

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

Но всё-таки, даже с учётом различающейся вариативности контекстов сравниваемых слов, какую-то картину всё же можно получить. Например, сравнивая выдачу по таким понятиям, как electric, software и experiment мы увидим чёткую картину по использованию понятия software, которое не применялось до середины XX века, немного размытую картину по понятию electric поскольку оно могло использоваться в различных смыслах и контекстах, вплоть до художественной литературы, и примерно одинаковый уровень для experiment. Видимо, эксперименты производили и обсуждали на страницах книг с 1800 года и до наших дней с примерно одинаковой частотой. В последние десятилетия даже чуть реже, чем раньше:

Гораздо более чёткую картину даёт использование уникальных слов-маркеров, для которых известно, в связи с чем и в какое время их могли употреблять, а в какое ещё не могли. Таким образом, кстати, можно ещё и проверить адекватность выдачи. Вот пример для Stalingrad, Sputnik и perestroyka:

Ну что же, все пики вроде бы на своих местах. Интереса к перестройке в мировой литературе было явно меньше, чем к первым запускам спутников Земли. Похоже на правду.

Если взять какие-то более размытые понятия, например, genetic и neural, то даже несмотря на какой-то шум от возможного использования этих слов в других смыслах, виден явный подъём с середины XX века:

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

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

Явно в начале здесь вклад самого Михаила Васильевича (наверное, какие-то ссылки на его работы), потом названного в честь него университета, а потом ещё города и суперкомпьютера.

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

По настройкам:

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

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

Какие на данный момент есть проблемы:

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

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

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

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

Какие промежуточные выводы можно сделать:

1) Объекты с уникальными и неизменными именами гораздо проще найти и оценить. Создавая новый язык программирования, программу или какое-то другое произведение давайте ему уникальное имя и старайтесь потом не менять.

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

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

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


В ходе работы пришла пара интересных мыслей:

1) Наверное, примерно так и должна быть устроена мировая библиотека будущего. Фактически, это уже её прототип.

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

Подробнее..

Создание SDK под Android в стиле Single-Activity

18.10.2020 04:15:28 | Автор: admin

Single activity подходом при создании конечного приложения под Android никого не удивишь. Но мы пошли дальше и использовали No-Activity при разработке SDK. Сейчас разберемся для чего это понадобилось, возникшие сложности и как их решали.

Стандартные 3rd party SDK в Android

Как обычно работают внешние SDK в Android? Открывается Activity библиотеки, выполняется некая работа, при необходимости возвращается результат в onActivityResult.

Стандартная схема работы SDK.Стандартная схема работы SDK.

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

Желаемый стек экранов приложения и SDKЖелаемый стек экранов приложения и SDK

Получается, что экраны нашего SDK должны быть частью внешнего приложения. Точно также, как вы можете использовать, например, MapFragment от Google. Итого, при стандартном подходе, мы сталкиваемся с рядом трудностей и ограничений.

Проблемы при стандартном подходе к SDK

  • Если вам нужно несколько взаимодействий между SDK и приложением, то придется открывать-закрывать Activity от SDK и аккуратно обрабатывать передачу данных туда-обратно.

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

  • При относительно долгом возможном нахождении в SDK внешнее приложение может уйти в Lock Screen. Такое может случиться, если Lock реализован на колбеках жизненного цикла Activity.

No-Activity подход при разработке SDK

Итак, мы решили, что основная проблема в том, что контекст (Activity) внешнего приложения и SDK разные. Отсюда следует резонное решение - отказаться от контекста SDK и во внешнее приложение поставлять только фрагменты. В таком случае разработчик сможет сам управлять стеком экранов.

No-Activity SDK на ФрагментахNo-Activity SDK на Фрагментах

Данный подход имеет как ряд плюсов, так и значительные минусы. Какие же?

Плюсы No-Acitivty SDK

  • Приложение и SDK имеют общий контекст, т.е. для пользователя это выглядит как абсолютно единое приложение.

  • Основное приложение имеет свой стек фрагментов, а SDK - свой через childFragmentManager.

  • Можно организовать любой порядок экранов и наложений элементов, т.к. навигация доступна и для внешнего приложения.

Минусы No-Acitivty SDK

  • Внешнее приложение должно изначально работать с фрагментами, желательно вообще быть Single-Activity.

  • У SDK нет своего контекста, если хотите использовать dagger - придется исхитриться (но это все же возможно).

  • SDK может влиять на внешнее Acitivty, т.к. requireActivity вернет именно его. Надо полностью доверять SDK.

  • Activity будет получать onActivityResult, и, вероятно, придется его прокидывать во фрагменты.

  • Разработчику внешнего приложения сложнее интегрировать SDK, т.к. простой вызов Activity уже не сработает.

Использование 3rd party библиотек внутри SDK

При любом подходе так или иначе придется использовать библиотеки внутри SDK. Это в свою очередь может привести к коллизии версий с внешним приложением. А части библиотек, например dagger2 нужен будет выделенный контекст.

Dagger2 внутри SDK

Для использования dagger зачастую в приложении используется класс Application. В случае с SDK так сделать не получится, потому что Application, вероятно, будет перетерт со стороны внешнего приложения.

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

internal object ComponentHolder {    lateinit var appComponent: SdkAppComponent        private set    @Synchronized    fun init(ctx: Context) {        if (this::appComponent.isInitialized) return        appComponent = DaggerSdkAppComponent            .builder()            .sdkAppModule(SdkAppModule(ctx))            .build()    }}

Остается только лишь понять, откуда вызвать init, да так, чтобы в процессе жизни SDK быть уверенным, что инициализация выполнилась до любой другой работы. Для этого можно использовать одну точку входа в SDK. Назовем ее EntryPointFragment. Данный фрагмент и будет виден внешнему приложению как единственная точка входа в SDK. Вся дальнейшая навигация внутри SDK будет происходить уже в нем через childFragmentManager.

Как раз при создании EntryPointFragment можно и инициализировать ComponentHolder для Dagger.

override fun onCreate(savedInstanceState: Bundle?) {        ComponentHolder.init(requireActivity())        ComponentHolder.appComponent.inject(this)        super.onCreate(savedInstanceState)    }

Итого, на выходе мы получили ComponentHolder, который можно использовать внутри SDK для инъекции нужных компонент.

Устранение коллизии в версиях

С данной проблемой столкнулись при обновлении версии okhttp3 до новой major версии 4.+. В ней добавили улучшенную поддержку Kotlin, в том числе, например, доступ к коду ошибки через code() теперь стало ошибкой. Клиенты SDK, используя либо 3, либо 4 версию должны получать ту же внутри SDK, иначе все сломается.

Это реально сделать, вынеся код с коллизиями в отдельный модуль. В нем будут 2 flavor:

    flavorDimensions("okhttpVersion")    productFlavors {        v3 {            dimension = "okhttpVersion"        }        v4 {            dimension = "okhttpVersion"        }    }        dependencies {        v3Api okhttp3.core        v3Api okhttp3.logging        v4Api okhttp4.core        v4Api okhttp4.logging}

В двух разных папках, отвечающих за каждый flavor будут одинаковые классы, один из которых будет использовать code() а другой code.

// Code in v3 folderclass ResponseWrapper(private val response: Response) {    val code : Int        get() = response.code()}
// Code in v4 folderclass ResponseWrapper(private val response: Response) {    val code : Int        get() = response.code}

Остается только на уровне приложения выбрать необходимый конфиг и дальше правильная версия приедет в финальный проект.

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

defaultConfig {...missingDimensionStrategy 'okhttpVersion', 'v4'}

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

Заключение

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

Подробнее..

Борьба за жизни переменных. Или как я попытался упростить жизнь Android разработчикам

17.03.2021 02:16:03 | Автор: admin

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

Идея

Идея появилась из проблемы. Проблема появилась из негодования.

Мне было жутко неприятно от мысли, что при повороте экрана приходится проделывать серьезную работу над тем, чтобы сохранить состояние экрана в том виде, каком оно было до поворота.
Но еще больше негодования у меня вызвала новость, что поворот экрана это не единственная причина, которая может мешать мне сохранять состояние в неизменном виде.
Я ранее слышал про то, что система подчищает ресурсы для чего-то, забивая на свернутые приложения, но когда я немного углубился в это, то очень сильно был огорчен. Именно из этого "негодования" у меня появилась проблема. Как мне сделать это красиво и не так болезненно?
И из этой проблемы появилась идея. Идея собрать все, что должно быть независимо от поворота экрана и высвобождения памяти, в одно место, чтобы я в этом месте был уверен на все сто процентов и всегда знал: Все, что я туда положил, там и останется в неизменном виде.

И не хотелось сильно размазывать код этими переменными...

Каким образом решить эту задачу? Сразу вспоминаешь решения, которые уже есть: bundle и т.д.

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

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

После анализа возможностей и моего знакомства с SavedStateHandle я загорелся желанием сделать обертку над ним и все это использовать в MVVM. Так и родилась библиотека которую сейчас Вы увидите.

Реализация

Ну тут все просто (имхо)

Например, мы уже определились со скоупом наших значений, которые мы хотим хранить. Тогда просто создаем класс в котором просто декларируем их и помечаем этот класс аннотацией @Unkillable

Например вот так:

@Unkillabledata class SampleFragmentState(    val testValue: Double,    val testLiveData: MutableLiveData<Double>) : EmptyState()

Также мы можем там указать и произвольные классы, но только они уже должны быть с Parcelize (Подробней).

Таким образом должен выглядить ViewModel. Естественно, библиотека предлагает не только работу с AndroidViewModel, но и просто ViewModel.

class SampleViewModel(    application: Application,    savedStateHandle: SavedStateHandle) : AndroidStateViewModel(application, savedStateHandle) {    override fun provideState() = createState<UnkillableSampleFragmentState>()}

UnkillableSampleFragmentState сгенерируется у Вас сразу после запуска билда проекта.

Естественно, наша ViewModel должна быть проинициализированна, но не совсем так как обычно. А так, как предлагает Google для использования SavedStateHandle.

activity?.application?.let { application ->     viewModel = ViewModelProvider(this, SavedStateViewModelFactory(application, this))        .get(SampleViewModel::class.java) }

Это все. Теперь можно использовать по назначению! Просто записываем туда данные и достаем их. Отмечу, что для сохранения класса необходимо делать его @Parcelize (Подробнее тут).

Вот таким образом можно воспользоваться библиотекой.

init {    // get values example    Log.d("StateLog", "0 value ${state.testValue}")    Log.d("StateLog", "1 value ${state.testLiveData?.value}")}fun onSetDataClicked() {    // set values example    state.testValue = 2.2    state.updateTestLiveDataValue(3.3) // yourLiveData.value = 3.3    state.postUpdateTestLiveDataValue(3.3) // yourLiveData.postValue(3.3)}

Таким образом, мы защищаемся от подчистки приложения из памяти операционной системой.

Итог

Цель данной библиотеки упростить разработку и разгрузить разработчика от написания кода, при условии, что он работает с сохранением состояния приложения. Также удалось очистить код от всех этих переменных, которые необходимо сохранить. К тому же теперь они логически отделены от общей массы и не засоряют код, что выглядит довольно приятно. Однако пока сейчас она работает только в MVVM от гугла.

GitHub

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru