Обычно, когда люди обсуждают «парадигму» языка программирования, они обсуждают, является ли это объектно-ориентированным программированием (ООП), функциональным программированием, процедурным программированием и т. д. Чтобы эффективно общаться, давайте определим, что будет означать «парадигма» в этом посте.

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

Давайте также дадим определение «экзистенциальному».
Экзистенциальный: экзистенциализм — это форма философского исследования, которое исследует характеристики существования. Экзистенциальный взгляд — это то, как вы видите само существование. Изменение всего лишь одной части экзистенциального взгляда приведет к изменению вашего взгляда на саму Вселенную.

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

Эти нюансы и отличия не всех интересуют, но меня они интересуют, и я надеюсь, что вам они тоже будут интересны.

Мы будем использовать код в качестве примеров!

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

Типы данных — одна из ключевых фундаментальных частей, составляющих парадигму языка. Они классифицируют данные и отличают их от других форм данных. Давайте сравним типы данных в Perl и Python, например.

Python имеет 4 примитивных типа данных:

  1. Строки («привет», «а», «5»)
  2. Целые числа (100, 42, 5)
  3. Поплавки ( 10.14, 3.14, 5.0 )
  4. Булевы значения (Истина, Ложь)

Чтобы проиллюстрировать эту парадигму на языке программирования Python, «5», 5.0 и 5 — это разные типы данных. «5» — это строка, 5.0 — это число с плавающей запятой, а 5 — это целое число. Вы можете доказать это, используя встроенную функцию type() в интерпретаторе Python, таком как iPython (или обычный интерпретатор будет работать).

(Обратите внимание, что In[x]: и Out[x]: взяты из интерпретатора iPython и не являются кодом, весь код идет после)

In [1]: type(5)
Out[1]: int

In [2]: type("5")
Out[2]: str

In [3]: type(5.0)
Out[3]: float

Сравните это с таким языком программирования, как Perl, и мы увидим другую парадигму типов данных. Perl имеет 3 примитивных типа данных:

  1. Скаляры («строки», 5 и ссылки ($scalarref = \$foo;))
  2. Массивы ( @array = (1, 2); )
  3. Хеши ( %hash = ( «1» =› «Дэви Джонс»); )

В Perl многие вещи могут быть «скалярами». Строка типа «5» является скаляром, 5.0 — скаляром, 5 — скаляром. Парадигма Perl не различает эти фрагменты данных так, как это делает Python. В глазах Perl это один и тот же тип данных.

В этом нет ничего «неправильного», но он показывает совершенно иной способ мышления, чем такой язык, как Python. Python гораздо более педантично относится к типу данных, чем Perl. Чтобы доказать это, давайте воспользуемся встроенной в Perl функцией ref() для некоторых переменных, чтобы проиллюстрировать, как Perl рассматривает типы данных.

Я использую интерпретатор Perl, встроенный в GNU/Linux. Вы можете получить доступ к этому интерпретатору, введя это в терминал:

perl -de 0

Давай начнем. (Обратите внимание, что DB‹1› взят из ввода в терминале и не является фактическим кодом, весь код находится после DB‹x›)

DB<1> use strict; use warnings;

  DB<2> $string = "a series of chars";

  DB<3> $int = 10;

  DB<4> %hash   = ( "1" => "Davy Jones" );

  # When using the ref() function in Perl, we have to
  # reference the variable with a \.
  # So, \$scalar is a reference to $scalar

  DB<5> print( ref( \$string ) );
  # output SCALAR

  DB<6> print( ref( \$int ) );
  # output SCALAR 
  # (notice that Perl sees an int and a string as the same)

  DB<12> print( ref( \%hash ) );
  # output HASH 
  # (Perl does distinguish hashes from scalars)

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

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

Парадигма итерации

Еще одно место, где мы видим различия в парадигме, — это итерация. На этой иллюстрации мы сравним итерацию с тремя языками программирования: Python, Perl и C.

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

  DB<1> @a1 = (1,2,3);

  DB<2> @a2 = (4,5,6);

  DB<3> @a3 = (7,8,9);

  DB<4> for $_(@a1, @a2, @a3) { print($_) };
  # 123456789

Эквивалентом массива в Perl является список в Python. Теперь у Python есть способы перебирать несколько списков, как это делает Perl с такими вещами, как *args, но это не было бы «эквивалентом» в парадигме Python. У Python совершенно другая парадигма, давайте напишем код для иллюстрации.

In [1]: a1 = [1,2,3]

In [2]: a2 = [4,5,6]

In [3]: a3 = [7,8,9]

In [4]: for each in [ a1, a2, a3 ]:
   ...:     print(each)
   ...:
# output is... 
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

Как видите, Python распечатал 3 списка, а не переменные в каждом списке одну за другой. Мы получили бы тот же результат, если бы заменили квадратные скобки на круглые скобки или полностью их удалили. Наблюдать.

In [5]: for each in  a1, a2, a3 :
   ...:     print(each)
   ...: 
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

In [6]: for each in  (a1, a2, a3) :
   ...:     print(each)
   ...: 
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

Опять же, мы МОГЛИ бы создать функцию на Python, используя *args для перебора каждого списка и получения того же результата, что и Perl, но это не эквивалентный код для представления парадигмы. Вот почему. Я думаю, что Perl сияет в итерациях со своей парадигмой, потому что, чтобы получить эквивалент в коде парадигмы, нам нужно было бы сделать это….

In [9]: for each in a1: print(each);
   ...: for each in a2: print(each);
   ...: for each in a3: print(each);
1
2
3
4
5
6
7
8
9

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

In [12]: def iterate(*args):
    ...:     for each in args:
    ...:         for e in each:
    ...:             print(e)
    ...: 
    ...: 

In [13]: iterate(a1, a2, a3)
1
2
3
4
5
6
7
8
9

Таким образом, хотя и в Perl, и в Python есть циклы for для итерации, их парадигма с этими циклами for совершенно различна. Но эти два примера не отражают всех различных парадигм в отношении итерации. Например, в C совершенно другая парадигма. Давайте проиллюстрируем это на некотором коде.

#include <stdio.h>

// Anything after // is a comment
// Let's print numbers from 1 to 10
int main() {
  int iteration;

  for (iteration = 1; iteration < 11; ++iteration)
  {
    printf("%d ", iteration);
  }
  return 0;
}

// output is:
// 1 2 3 4 5 6 7 8 9 10

В таком языке, как C, нет парадигмы «для каждого в итерации». C имеет совершенно другую парадигму, где вы должны инициализировать начальное значение, иметь условие while, а затем операцию.

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

Парадигма назначения

Языки программирования также имеют разные экзистенциальные взгляды на присваивание. В присваиваниях есть явные и неявные присваивания. Python является неявным, что означает, что вы присваиваете данные имени переменной, а Python выясняет, что это такое. Чтобы определить целое число в Python, вы можете просто сказать:

integer = 6

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

#include <stdio.h>

int main() {

  // notice no declaration of data type
  // this code will give an error
  i = 5;
  printf("%d", i);

  return 0;
}

Если вы попытаетесь работать с этим кодом, вы получите что-то вроде следующего:

gcc /tmp/ezyISLlADo.c -lm
/tmp/ezyISLlADo.c: In function 'main':
/tmp/ezyISLlADo.c:6:1: error: 'i' undeclared (first use in this function)
    6 | i = 5;
      | ^
/tmp/ezyISLlADo.c:6:1: note: each undeclared identifier is reported only once for each function it appears in

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

#include <stdio.h>

int main() {

  // notice the declaration of int
  int i = 5;
  printf("%d", i);

  return 0;
}

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

fn main() {

    // notice the mut keyword
    // this declares a variable to be mutable

    let mut x = 5;

    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

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

В таком языке, как Java, есть понятие «общедоступных», «приватных» и «защищенных» переменных. Эти метки управляют доступом к переменным. Этой концепции не существует в таком языке, как Python.

Еще одно отличие — несколько заданий. В Python и других языках вы можете сделать что-то вроде этого:

x = y = 5

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

Заключение

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

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

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