Авито программирование

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

СОДЕРЖАНИЕ
• Как Мы Можем Классифицировать Языки? • Категории языков программирования • Машинные языки • Языки ассемблера • Языки высокого уровня • Системные языки • Языки сценариев • Эзотерические языки • “Два вида” • Парадигмы программирования • Резюме

Цели подразделения

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

Как Мы Можем Классифицировать Языки?

Существует по крайней мере два способа перечисления языков программирования:

Но если мы хотим КЛАССИФИЦИРОВАТЬ языки. Нам нужно взглянуть на внешний вид языка. Его модель выполнения или тип парадигм программирования. Которые наиболее естественно поддерживаются.

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

Категории языков программирования

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

  • Машинные языки, которые интерпретируются непосредственно в аппаратном обеспечении
  • Языки ассемблера, которые являются тонкими оболочками над соответствующим машинным языком
  • Языки высокого уровня, которые не зависят от машины
  • Системные языки, предназначенные для написания низкоуровневых задач. Таких как управление памятью и процессами

  • Скриптовые языки, которые. Как правило. Являются чрезвычайно высокоуровневыми и мощными
  • Доменные языки, которые используются только в узкоспециализированных областях
  • Визуальные языки, которые не основаны на тексте
  • Эзотерические языки, которые на самом деле не предназначены для использования. Но очень интересны. Забавны или образовательны в некотором роде

Эти типы не являются взаимоисключающими: Perl является как высокоуровневым. Так и скриптовым; C считается как высокоуровневым. Так и системным. Некоторые языки частично визуальны. Но вы можете вводить биты кода в маленькие коробки.

Другие типы. Которые люди определили: Игрушечный, Образовательный, Очень высокий уровень, Авторский, Компилируемый, Интерпретируемый, Свободная форма, Фигурная скобка, Аппликативный, Гомоиконический, Фон Нейман, Ориентированный на выражение, Постоянный, Параллельный, Поток данных, Массив, Стековый, Конкатенативный, Действие, Реактивный. Ограничение, Клей. Рефлексивный, Запрос. Промежуточный. Квантовый, Гибридный. Встраиваемый, Макро. Тактильный. См. Страницу категорий Википедии о классификации языков программирования.

Машинные языки

Машинный язык-это прямое представление кода и данных. Выполняемых непосредственно вычислительным устройством.

Функция машинных языков:

  • Регистры для хранения значений и промежуточных результатов
  • Очень низкоуровневые машинные инструкции (add,sub,div,sqrt), которые работают с этими регистрами и/или памятью
  • Метки и условные переходы для выражения потока управления
  • Отсутствие поддержки управления памятью — программисты делают это сами

Машинные инструкции выполняются в аппаратном обеспечении машины. Поэтому машинный код по определению зависит от машины. Разные машины имеют разные наборы команд.

Инструкции и их операнды — это всего лишь биты.

Машинный код обычно пишется в шестнадцатеричном формате. Вот пример архитектуры Intel 64:

89 F8 A9 01 00 00 00 75 06 6B C0 03 FF C0 C3 C1 E0 02 83 E8 03 C3

Вы можете сказать. Что он делает?

Как разрабатываются наборы машинных инструкций?

Многие машинные языки. По-видимому. Просто брошены вместе с большим количеством инструкций общего назначения. Но существуют процессоры. Разработанные специально для выполнения реализации языков высокого уровня. Обязательно прочтите о серии Borroughs 5000 и ее преемниках, а также о Intel 432.

Языки ассемблера

Язык ассемблера — это кодирование машинного кода в нечто более читаемое.

Он присваивает удобочитаемые метки (или имена) местам хранения. Целям перехода и начальным адресам подпрограмм. Но на самом деле не выходит слишком далеко за их пределы. Он действительно изоморфен своему машинному языку. Вот функция сверху на архитектуре Intel 64 с использованием языка ассемблера GAS:

        .глобл Ф
       
.текст
Ф
:
мова
%эод, в регистре%EAX      # положите первый параметр в регистре EAX
тест
$1, %еах        # проверить младший бит
jnz нечетное
# если это не нуль. Перейти к странным


imul
$3, %еах        # это даже, так что умножить его на 3
іпсбыл
в регистре %eax            # и добавить 1
рет
# и вернуть его
странным
:
шл
$2, %еах         # это странно, поэтому умножаем на 4
подгруппы
$3, %еах         # и вычесть 3
рет
# и вернуть его

И вот такая же функция. Написанная для SPARC:

        .global f
f
:
andcc
%o0, 1, %g0
bne
.L1
sll
%o0, 2, %g2
sll
%o0, 1, %g2
add
%g2, %o0, %g2
b
.L2
add
%g2, 1, %o0
.L1:
add
%g2, -3, %o0
.L2:
retl
nop

Языки высокого уровня

Язык высокого уровня избавляется от всех ограничений конкретной машины. HLLS может иметь такие функции, как:

  • Имена почти для всего: переменные, типы. Подпрограммы. Константы, модули

  • Сложные выражения (например,2 * (y^5) >= 88 && sqrt(4.8) / 2 % 3 == 9)
  • Структуры управления (условные обозначения. Переключатели, петли)
  • Составные типы (массивы, структуры)
  • Объявления типов
  • Проверка типа
  • Простые, часто неявные способы управления глобальным. Локальным и кучным хранилищем
  • Подпрограммы с их собственной частной областью действия
  • Абстрактные типы данных, модули. Пакеты, классы
  • Исключения

Предыдущий пример выглядит так в Fortran 77 (обратите внимание. Как код начинается в столбце 7 или выше):

 ЦЕЛОЧИСЛЕННАЯ ФУНКЦИЯ F(N)
INTEGER N
       
IF (MOD(N, 2) .EQ. 0) THEN
F
= 3 * N + 1
       
ELSE
F
= 4 * N - 3
       
END IF
       
RETURN
       
END

и вот так в Fortran 90 (где требования к колонкам были окончательно сняты):

целочисленная функция f (n)
   
implicit none
integer
, intent(in) :: n
   
if (mod(n, 2) == 0) then
f
= 3 * n + 1
   
else
f
= 4 * n - 3
   
end if
end function

и вот так в Аде:

функция F (N: Integer) return Integer is
begin
   
if N mod 2 = 0 then
       
return 3 * N + 1;
   
else
       
return 4 * N - 3;
   
end if;
end F;

и вот так в C и C++:

int f(const int n) {
   
return (n % 2 == 0) ? 3 * n + 1 : 4 * n - 3;
}

и вот так в Java и C#:

класс ThingThatHoldsTheFunctionUsedInTheExampleOnThisPage {
   
public static int f(int n) {
       
return (n % 2 == 0) ? 3 * n + 1 : 4 * n - 3;
   
}
}

и вот так в Скале:

def f(n: Int) = if (n % 2 == 0) 3 * n + 1 else 4 * n - 3;

и вот так в Котлине:

fun f(n: Int) = if (n % 2 == 0) 3 * n + 1 else 4 * n - 3

и вот так в JavaScript:

функция f(n) {
 
return (n % 2 === 0) ? 3 * n + 1 : 4 * n - 3;
}

и вот так в CoffeeScript:

f = (n) ->> если n % 2 == 0 , то 3 * n - 1 еще 4 * n + 3

и вот так в Smalltalk:

f
 
^self % 2 = 0 ifTrue:[3 * self + 1] ifFalse:[4 * self - 3]

и вот так в стандартном МЛ:

fun f n = if n mod 2 = 0 then 3 * n + 1 else 4 * n - 3

и вот так в Вязе:

f n = if n % 2 == 0 then 3 * n + 1 else 4 * n - 3

и вот так в Haskell (спасибо @kaftoot):

f n | even(n) = 3 * n + 1  | в противном случае = 4 * n - 3

и вот так в Юлии (да, 3n-это “трижды n”):

f(n) = iseven(n) ? 3n+1 : 4n-3

и вот так на шепелявом:

(defun f (n)
 
(если (= (mod n 2) 0)
   
(+ (* 3 n) 1)
   
(- (* 4 n) 3)))

и вот так в Clojure:

(defn f [n]
 
(если (= (mod n 2) 0)
   
(+ (* 3 n) 1)
   
(- (* 4 n) 3)))

и вот так в Прологе:

f(N, X) :- 0 -mod(N, 2), X- 3 * N + 1.
f
(N, X) :- 1 -mod(N, 2), X- 4 * N - 3.

и вот так в Эрланге:

f(N) когда (N) 1) == 0 ->> 3 * N + 1;
f
(N) ->> 4 * N - 3.

и вот так в Perl:

sub f {
   
my $n = shift;
$n
% 2 == 0 ? 3 * $n + 1 : 4 * $n - 3;
}

и вот так в Python:

def f(n):
   
возврат 3 * n + 1 , если n % 2 == 0 еще 4 * n - 3

и вот так в Рубине:

def f(n)
n
% 2 == 0 ? 3 * n + 1 : 4 * n - 3;
конец

и вроде этого в Го:

func f(n int) int {
   
if n % 2 == 0 {
       
return 3 * n + 1
   
} else {
       
return 4 * n - 3
   
}
}

и вот так в Ржавчине:

fn f(n: int)  int {
   
return if n % 2 == 0 {3 * n + 1} else {4 * n - 3}
}

и вот так в Свифте:

func f(n: Int)  Int {
   
return n % 2 == 0 ? 3 * n + 1 : 4 * n - 3
}

и вот так в К:

f:{:[x!2;(4*x)-3;1+3*x

Упражнение: Какой из этих языков требует. Чтобы переменные или функции объявлялись с типами. А какой-нет?

Упражнение: Реализуйте эту функцию в PHP. Objective C, Ceylon. D и Mercury.

Системные языки

Языки системного программирования отличаются от языков прикладного программирования тем. Что они больше связаны с управлением компьютерной системой. А не с решением общих проблем в здравоохранении. Играх или финансах. В системном языке программист. А не система времени выполнения. Как правило. Несет ответственность за:

  • Управление памятью
  • Управление процессами
  • Передача данных
  • Тайники
  • Драйверы устройств
  • Непосредственное взаимодействие с операционной системой

Скриптовые языки

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

Эзотерические языки

Эзотерический язык не предназначен для того. Чтобы его воспринимали всерьез. Они могут быть шутливыми. Почти минималистичными или деспотическими (намеренно запутанными или недетерминированными).

См. статью Википедии об эзотерических языках.

Упражнение: Реализуйте описанную выше функцию в False, Brainfuck. Befunge, Malbolge. Kipple и reMorse.

— Два Вида”

Дихотомия Остерхаута

Джон Остерхаут однажды заявил. Что языки программирования примерно делятся на два типа. Которые он назвал скриптовыми и системными языками. Вы можете прочитать об этой идее в Википедии. Затем прочтите эту двухчастную статью (Часть 1, Часть 2) о дихотомии и о языках. Которые. Кажется. Отвергают ее.

Алан Кей

Языки программирования можно классифицировать несколькими способами: императивными. Прикладными. Логическими. Проблемно-ориентированными и т. д. Но все они кажутся либо Вероятно. Не случайно. Что все агглютинативные языки. По-видимому. Были спровоцированы комитетами. А языки кристаллизации-одним человеком.

Бьярне Страуструп

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

Парадигмы программирования

Очень часто язык программирования создается для того. Чтобы помочь людям программировать определенным образом. Парадигма программирования-это стиль или “способ” программирования. Некоторые языки позволяют легко писать в одних парадигмах. Но не в других.

Никогда не используйте фразу “парадигма языка программирования.”

Парадигма-это способ делать что-то (например. Программирование). А не конкретная вещь (например, язык). Теперь верно. Что если язык программирования L делает определенную парадигму программирования P легко выражаемой. То мы часто говорим: “L-это язык P” (например. “Haskell-это функциональный язык программирования”). Но это не означает. Что существует такая вещь. Как “функциональная языковая парадигма”.

Вы должны знать эти общие парадигмы:

  • Императив: Программирование с явной последовательностью команд. Обновляющих состояние.
  • Декларативность: Программирование путем указания желаемого результата. А не способа его получения.
  • Структурированное: Программирование с чистыми. Свободными от гото. Вложенными структурами управления.
  • Процедурное: Императивное программирование с вызовами процедур.
  • Функциональное (прикладное): Программирование с вызовами функций. Которые избегают любого глобального состояния.
  • Функциональный уровень (комбинатор): Программирование вообще без переменных.
  • Объектно-ориентированный: Программирование путем определения объектов. Которые посылают сообщения друг другу. Объекты имеют свои собственные внутренние (инкапсулированные) состояния и открытые интерфейсы. Ориентация объекта может быть:

    • Объекты получают состояние и поведение на основе членства в классе.
    • Прототип на основе: Объекты получают поведение от объекта-прототипа.
  • Event-Driven: Программирование с помощью излучателей и слушателей асинхронных действий.
  • Потоковое управление: Программирование процессов. Взаимодействующих друг с другом по заранее определенным каналам.
  • Логика (основанная на правилах): Программирование путем указания набора фактов и правил. Двигатель выводит ответы на вопросы.
  • Ограничение: Программирование путем задания набора ограничений. Механизм находит значения. Соответствующие ограничениям.
  • Аспектно-ориентированный: Программирование межсекторальных проблем применяется прозрачно.
  • Рефлексивное: Программирование путем манипулирования самими элементами программы.
  • Массив: Программирование с помощью мощных операторов массива. Которые обычно делают циклы ненужными.

Парадигмы не должны быть взаимоисключающими; одна программа может включать в себя несколько парадигм!

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

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

Императивное программирование

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

 результат = []
Я
= 0
начало
:
numPeople
= длина(людей)
   
случае. Если я >= numPeople перейти готовой
П
= Люди[я]
nameLength
= длина(п.имя)
   
, если nameLength 5 Гото nextOne
upperName
= toUpper(п.наименование)
addToList
(результат, upperName)
nextOne
:
я
= я + 1
   
Гото начать
закончил
:
   
вернуться рода(результат)

Структурированное программирование

Структурированное программирование-это разновидность императивного программирования. Где поток управления определяется вложенными циклами. Условными обозначениями и подпрограммами. А не через goto. Переменные обычно являются локальными для блоков (имеют лексическую область действия).

result = [];
for i = 0; i length(people); i++ {
p
= people[i];
   
if length(p.name)) >> 5 {
addToList
(result, ToUpper(p.name));
   
}
}
return sort(result);

Ранние языки. Подчеркивающие структурированное программирование: Algol 60, PL/I, Algol 68, Pascal, C, Ada 83, Modula, Modula-2. Структурированное программирование как дисциплина иногда, однако. Было начато знаменитым письмом Эдсгера Дейкстры. Озаглавленным

Объектно-ориентированное программирование

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

Одним из наиболее заметных аспектов более чистых ОО-языков является то. Что условные выражения и циклы сами становятся сообщениями. Аргументами которых часто являются блоки исполняемого кода. В синтаксисе. Подобном Smalltalk:

result := List new.
people each
: [:p |
p name length GreaterThan
: 5 ifTrue: [result add (p name upper)]
]
result sort
.
^result

Это можно сократить до:

^people filter: [:p | p name length GreaterThan: 5] map: [:p | p name upper] sort

Многие популярные языки. Которые называют себя языками OO (например, Java, C++). На самом деле просто берут некоторые элементы OOP и смешивают их с императивным кодом. В следующем мы можем видеть. Что lengthand toUpperявляются методами. А не функциями верхнего уровня, но forand ifснова становятся структурами управления:

result = []
for p in people {
   
if p.name.length >> 5 {
result
.add(p.name.ToUpper);
   
}
}
return result.sort;

Первым объектно-ориентированным языком был Simula-67; Smalltalk вскоре последовал за первым “чистым” объектно-ориентированным языком. Многие языки, разработанные с 1980-х годов по настоящее время, обозначили себя объектно-ориентированными, в частности C++, CLOS (object system of Common Lisp), Eiffel, Modula-3, Ada 95, Java, C#, Ruby.

Упражнение: Изучите, как эволюционировал термин “объектно-ориентированный” и существуют ли какие-то противоречия вокруг его значения. Возможно, вы захотите начать свой рассказ со знаменитой цитаты Алана Кея: “Я придумал термин Что у него на уме? Неужели его так глубоко волнует. Как C++ и Java отклонились от предполагаемого значения?

Декларативное программирование

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

выберите upper(name)
from people
where length(name) >> 5
order by name

Никаких циклов. Никаких назначений и т. Д. Любой движок. Который интерпретирует этот код. Просто должен получить желаемую информацию и может использовать любой подход. Какой захочет. (Парадигмы логики и ограничений. Как правило. Также декларативны.)

Функциональное программирование

В функциональном программированиипоток управления выражается объединением вызовов функций. А не присвоением значений переменным:

сортировка(
исправить
(ф п
ifThenElse
(равно(п, emptylist),
emptylist
,
ifThenElse
(большой(длина(наименование(головки(П))), 5),
добавить
(to_upper(наименование(головки(п))), Ф(хвост(п))),
ф
(хвост(людям)))))(людей))

Ой! Мы опишем это позже. А пока будьте благодарны. Что обычно есть синтаксический сахар:

пусть
   
fun uppercasedLongNames [] = []
     
| uppercasedLongNames (p :: ps) =
         
если length(name p) >> 5 то (to_upper(name p))::(uppercasedLongNames ps)
         
else (uppercasedLongNames ps)
in
sort
(uppercasedLongNames(people))

А? Это все равно не очень красиво. Почему людям это нравится? Что ж, реальная сила этой парадигмы заключается в передаче функций функциям (и возврате функций из функций).

сортировка(
фильтр
(s =>> длина s >> 5,
карта
(p =>> to_upper(имя p),
люди
)))

Мы можем сделать лучше. Используя прохладный |>оператор. Вот x |> fтолько значит f(x). Оператор имеет очень низкий приоритет. Поэтому вы можете читать вещи слева направо:

люди |>> карта (p =>> to_upper (имя p)) |>> фильтр (s =>> длина s >> 5) |>> сортировка

Давайте продолжим! Заметьте , что вы не стали бы писатьmap(s => square(x)), верно? Ты бы написал map(square). Мы можем сделать что-то подобное выше. Но мы должны использовать функциональную композицию, вы знаете, (f o g)xэто f(g(x))так:

people  map (to_upper o name)  filter (s  length s > 5)  sort

Вот три вещи. Которые нужно прочитать. Чтобы понять суть функционального программирования:

С функциональным программированием:

  • Нет никаких команд. Только побочные эффекты свободных выражений
  • Код намного короче. Менее подвержен ошибкам и намного легче доказать правильность
  • Существует больше присущего параллелизма. Поэтому хорошие компиляторы могут производить более быстрый код

Некоторые люди любят говорить:

  • Функциональное. Или прикладное. Программирование-это программирование без операторов присваивания: к аргументам просто применяются функции. Примеры: Scheme, Haskell. Miranda, ML.
  • Программирование на уровне функций устраняет переменные; функции объединяются с функционалами, то есть комбинаторами . Примеры: FP, FL, J.

Упражнение: Напишите приведенный выше пример в формулах Miranda, ML и J.

Упражнение: Исследуйте следующие стили программирования и определите. Насколько они похожи и чем отличаются друг от друга: (а) Стековые. (б) Конкатенативные. (в) точечные. (г) Молчаливые.

Во многих языках есть аккуратная маленькая вещь. Называемая пониманием, которая сочетает в себе карту и фильтр.

sorted(p.name.upper() for p in people if len(p.name) > 5)

Логика и программирование ограничений

Логическое программирование и программирование ограничений-это две парадигмы . В которых программы строятся путем установления отношений. Которые задают факты и правила вывода, и спрашивают. Является ли что-то истинным (т. Е.) Объединение и отступление для поиска решений (то есть удовлетворения целей) происходит автоматически.

Языки. Подчеркивающие эту парадигму: Prolog, GHC, Parlog. Vulcan, Polka. Mercury, Fnil.

Упражнение: Напишите бегущий пример в прологе.

Языки и парадигмы

Одной из характеристик языка является его поддержка определенных парадигм программирования. Например, Smalltalk имеет прямую поддержку объектно-ориентированного программирования. Поэтому его можно назвать объектно-ориентированным языком. Программы OCaml, Lisp. Scheme и JavaScript. Как правило. Широко используют передачу функций. Поэтому их называют “функциональными языками”. Несмотря на наличие переменных и многих императивных конструкций.

Здесь есть два очень важных замечания:

  • Очень немногие языки реализуют парадигму на 100%. Когда они это делают, они чисты. Невероятно редко встречается “чистый ООП” язык или “чистый функциональный” язык. Многие языки имеют несколько побегов; например. В OCaml вы будете программировать с функциями 90% или более времени. Но если вам нужно состояние. Вы можете получить его. Другой пример: очень немногие языки реализуют ООП так. Как это представлял себе Алан Кей.

    “ООП для меня означает только обмен сообщениями. Локальное удержание и защиту и сокрытие состояния-процесса. А также крайнюю позднюю привязку всех вещей. Это можно сделать как на Smalltalk. Так и на LISP. Возможно, есть и другие системы. В которых это возможно. Но я не знаю о них.”

  • Множество языков облегчат программирование в одной или нескольких парадигмах. В Scala вы можете довольно легко выполнять императивное. Объектно-ориентированное и функциональное программирование. Если язык специально разработан для программирования во многих парадигмах. Он называется многопарадигмальным языком. Если язык только случайно поддерживает несколько парадигм. У нас нет специального слова для этого.

Краткие сведения

Мы покрыли:

  • Различные способы перечисления и/или классификации языков
  • Многие категории языков программирования
  • Сколько языков “посмотрите”
  • Ряд парадигм программирования