В чем разница между декларативным и императивным программированием? [закрыто]

541

Я искал в Интернете поиски определения декларативного и императивного программирования, которое бы пролило свет на меня. Тем не менее, язык, используемый на некоторых ресурсах, которые я нашел, устарел - например, в Википедии . У кого-нибудь есть реальный пример того, что они могли бы показать мне, что может дать некоторую перспективу к этому вопросу (возможно, в C #)?

штифтик
источник
3
Императив идет в ресторан и заказывает 6 унций. стейк (приготовленный редко), картофель фри (с кетчупом), гарнир (с ранчо) и кола (без льда). Официант доставляет именно то, что он просил, и он взимает $ 14.50. С другой стороны, Декларатив идет в ресторан и говорит официанту, что он хочет заплатить только около 12 долларов за ужин, и он в настроении для стейка. Официант возвращается с 6 унциями. стейк (приготовленная среда), картофельное пюре, брокколи на пару, обеденный рулет и стакан воды. С него взимается 11,99 долларов.
cs_pupil

Ответы:

786

Отличным примером декларативного и императивного программирования на C # является LINQ.

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

Например, давайте начнем с этой коллекции и выберем нечетные числа:

List<int> collection = new List<int> { 1, 2, 3, 4, 5 };

С императивным программированием мы должны были пройти через это и решить, чего мы хотим:

List<int> results = new List<int>();
foreach(var num in collection)
{
    if (num % 2 != 0)
          results.Add(num);
}

Здесь мы говорим:

  1. Создать коллекцию результатов
  2. Шаг за каждым номером в коллекции
  3. Проверьте число, если оно нечетное, добавьте его к результатам

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

var results = collection.Where( num => num % 2 != 0);

Здесь мы говорим «Дайте нам все, где это странно», а не «Пошаговое выполнение коллекции. Проверьте этот элемент, если он нечетный, добавьте его в коллекцию результатов».

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

Рид Копси
источник
8
+1. Тем не менее, вы сначала упоминаете LINQ, но при чем здесь примеры?
Зано
15
collection.Where использует декларативные методы расширения LINQ. Это не использование возможностей языка C #, а декларативный API. Я не хотел смешивать сообщения здесь, поэтому я избегал языковых дополнений, созданных поверх декларативных методов.
Рид Копси
242
Мне кажется, что декларативное программирование - это не более чем уровень абстракции.
Дражен Бжеловук
8
Это хороший ответ, но он отвечает на разницу между нечистым функциональным и императивным программированием. collection.Whereне использует декларативный синтаксис, который предоставляет Linq - см. msdn.microsoft.com/en-us/library/bb397906.aspx для примеров, from item in collection where item%2 != 0 select itemбудет декларативная форма. Вызов функции не становится декларативным программированием только потому, что эта функция находится в пространстве имен System.Linq.
Пит Киркхэм,
30
@PeteKirkham Синтаксис, который вы используете, не является проблемой - декларативный или императивный - это скорее объявление о том, что вы хотите, а не объяснение того, как это должно происходить. Использование встроенного синтаксиса или синтаксиса метода расширения является отдельной проблемой.
Рид Копси
145

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

Простой пример в Python:

# Declarative
small_nums = [x for x in range(20) if x < 5]

# Imperative
small_nums = []
for i in range(20):
    if i < 5:
        small_nums.append(i)

Первый пример является декларативным, потому что мы не указываем никаких «деталей реализации» построения списка.

Чтобы связать пример C #, обычно использование LINQ приводит к декларативному стилю, потому что вы не говорите, как получить то, что вы хотите; ты говоришь только то, что хочешь. То же самое можно сказать и о SQL.

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

SELECT score FROM games WHERE id < 100;

«Компилятор» SQL может «оптимизировать» этот запрос, потому что он знает, что idэто индексированное поле - или, может быть, оно не проиндексировано, и в этом случае ему все равно придется перебирать весь набор данных. Или, может быть, движок SQL знает, что сейчас самое время использовать все 8 ядер для быстрого параллельного поиска. Вы , как программист, не заботитесь ни об одном из этих условий, и вам не нужно писать свой код для обработки какого-либо особого случая таким образом.

Марк Рушаков
источник
30
Этот пример Python НЕ является декларативным.
Хуанхо Конти
18
@Juanjo: Это IS decalarative.
фактор
3
Как первое утверждение здесь более декларативно, чем второе?
Зенна
17
Согласие с Juanjo и zenna - конструкция цикла не превращается волшебным образом в декларативную программу при рефакторинге в более короткую запись.
Феликс Франк
11
Не согласен с @FelixFrank и склоняется к «смелому» утверждению @ missingfaktor. Традиционный, «полностью» декларативный способ сделать это - filter(lambda x: x < 5, range(20))это просто еще один рефакторинг в более короткие обозначения. Это ничем не отличается от выражения понимания списка (которое имеет четкие разделы «карта» и «фильтр»), которое было создано (см. Pep 202 ) с явным намерением создать более краткую запись. И это понимание списка было бы более ясным / идиоматическим в этом случае.
yoniLavi
100

Декларативный и императивный

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

Декларативное программирование : это парадигма программирования, которая выражает логику вычислений (что делать) без описания потока управления (как это делается). Некоторые хорошо известные примеры декларативных доменных языков (DSL) включают CSS, регулярные выражения и подмножество SQL (например, запросы SELECT). Многие языки разметки, такие как HTML, MXML, XAML, XSLT ... часто являются декларативными. Декларативное программирование пытается стереть различие между программой как набором команд и программой как утверждением о желаемом ответе.

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

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

В следующем примере императивного программирования в MSDN циклы по числам от 1 до 10 и поиск четных чисел.

var numbersOneThroughTen = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//With imperative programming, we'd step through this, and decide what we want:
var evenNumbers = new List<int>();
foreach (var number in numbersOneThroughTen)
{    if (number % 2 == 0)
    {
        evenNumbers.Add(number);
    }
}
//The following code uses declarative programming to accomplish the same thing.
// Here, we're saying "Give us everything where it's odd"
var evenNumbers = numbersOneThroughTen.Select(number => number % 2 == 0);

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

Али Карими
источник
7
Можете ли вы добавить описание для объектно-ориентированного?
Мбиграс
1
Хорошие примеры: tylermcginnis.com/imperative-vs-declarative-programming
user3245268
54

Все вышеперечисленные ответы и другие посты онлайн упоминают следующее:

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

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

  • Например, LINQ более декларативен, чем циклы (for, while и т. Д.), Например, вы можете использовать list.Where()для получения нового отфильтрованного списка. Чтобы это работало, Microsoft сделала всю тяжелую работу за абстракцией LINQ.

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

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

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

// JavaScript example

// Least declarative
const bestProducts = [];
for(let i = 0; i < products.length; i++) {
    let product = products[i];
    if (product.rating >= 5 && product.price < 100) {
        bestProducts.push(product);
    }
}


// More declarative
const bestProducts = products.filter(function(product) {
    return product.rating >= 5 && product.price < 100;
});

// Most declarative, implementation details are hidden in a function
const bestProducts = getBestProducts();

PS крайность декларативного программирования состоит в том, чтобы изобрести новые языки, специфичные для предметной области (DSL):

  1. Поиск строки : регулярное выражение вместо пользовательского императивного кода
  2. React.js : JSX вместо прямой манипуляции с DOM
  3. AWS CloudFormation : YAML вместо CLI
  4. Реляционная база данных : SQL вместо старых API для чтения и записи, таких как ISAM или VSAM.
engineforce
источник
Есть много хороших примеров декларативного программирования: React , CloudFormation , Terraform
engineforce
Таким образом, «декларативное» программирование просто означает перемещение кода, выполняющего работу, в функцию?
Гийом Ф.
@GuillaumeF. Речь идет о создании предметной абстракции. Например, - в банковском деле: вы должны создавать такие функции, как debit, depositи т. Д. Вместо повторения беспристрастного кодаaccount.balance += depositAmount
engineforce
44

Я добавлю еще один пример, который редко появляется при обсуждении декларативного / императивного программирования: пользовательский интерфейс!

В C # вы можете создать пользовательский интерфейс, используя различные технологии.

В конце концов, вы можете использовать DirectX или OpenGL, чтобы очень настоятельно рисовать ваши кнопки, флажки и т. Д. Построчно (или действительно, треугольник за треугольником). Вам решать, как нарисовать пользовательский интерфейс.

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

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

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

Суть всего этого:

Декларативный -> whatВы хотите сделать

Императив -> howВы хотите, чтобы это было сделано

Эрих Мирабал
источник
27

Мне понравилось объяснение из кембриджского курса + их примеры:

  • Декларативный - укажите, что делать, а не как это делать
    • Например: HTML описывает, что должно отображаться на веб-странице, а не то, как это должно быть нарисовано на экране.
  • Обязательно - укажите, что и как
    • int x; - что (декларативно)
    • x=x+1; - как
ROMANIA_engineer
источник
«не так, как оно должно быть обращено на экране» ... Так что же это означает , что CSSэто императив тогда?
Chef_Code
11
Нет. Это также можно считать декларативным, потому что вы просто говорите, что хотите - «сделайте эту ячейку синей» . Представьте, что вы хотите нарисовать одну и ту же границу с помощью императивного подхода (например, JavaScript). Затем вам нужно сказать «перейти к точке (x1, y1), провести синюю линию между этой точкой и (x2, y1), нарисовать синюю линию от (x2, y1) до (x2, y2), нарисовать синюю линию от (x2, y2) до (x1, y2) нарисуйте синюю линию от (x1, y2) до (x1, y1) " .
ROMANIA_engineer
@ROMANIA_engineer, где можно найти такой кембриджский курс, пожалуйста?
тестовая команда
@testteam, поищите в Google "cl.cam.ac.uk". Вы можете изменить годы из URL.
ROMANIA_engineer
@ROMANIA_engineer, понял, спасибо
тестовая команда
26

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


Вы можете посмотреть на каждую часть инструкции как где-то в континууме:

Степень абстракции:

Declarative <<=====|==================>> Imperative

Пример декларативного реального мира:

  1. Библиотекарь, пожалуйста, посмотрите мне копию Моби Дика. (Библиотекарь по своему усмотрению выбирает лучший способ выполнения запроса)

Императивный пример реального мира:

  1. Перейти в библиотеку
  2. Система организации поиска книг (Каталог карт - Старая школа)
  3. Узнайте, как пользоваться карточными каталогами (вы тоже забыли)
  4. Выясните, как полки маркированы и организованы.
  5. Выясните, как книги организованы на полке.
  6. Расположение перекрестной справочной книги из карточного каталога с системой организации, чтобы найти указанную книгу.
  7. Взять книгу в кассу системы.
  8. Проверьте книгу.
Lucent Fox
источник
Разве это не абстракция, а декларатив / императив? Вы все еще учите библиотекаря принести книгу.
kamathln
Обновленный ответ, чтобы быть более полным и включить этот аспект в решение.
Lucent Fox
3
В этом есть доля правды, но это не полное определение. При декларативном программировании вы указываете конечную цель, не обращая внимания на отправную точку. При обязательном программировании важна определенная начальная точка. Это как разница между указанием адреса и указанием направления. Адрес полезен, где бы вы ни были. Принимая во внимание, что указания недействительны, если вы начинаете где-то еще.
Ктхуту
24

Императивное программирование требует, чтобы разработчики шаг за шагом определяли, как должен выполняться код. Чтобы дать указания в обязательном порядке, вы говорите: «Идите на 1-ю улицу, поверните налево на улицу Майн, проезжайте два квартала, поверните направо на клен и остановитесь у третьего дома слева». Декларативная версия может звучать примерно так: «Поезжай к дому Сью». Один говорит, как сделать что-то; другой говорит, что нужно сделать.

Декларативный стиль имеет два преимущества перед императивным стилем:

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

Calvert, C Kulkarni, D (2009). Essential LINQ. Аддисон Уэсли. 48.

GorkemHalulu
источник
11

Императивное программирование явно говорит компьютеру, что и как делать, например, указание порядка и т. Д.

C #:

for (int i = 0; i < 10; i++)
{
    System.Console.WriteLine("Hello World!");
}

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

C # является гораздо более императивным языком программирования, но некоторые функции C # являются более декларативными, как Linq

dynamic foo = from c in someCollection
           let x = someValue * 2
           where c.SomeProperty < x
           select new {c.SomeProperty, c.OtherProperty};

То же самое можно написать императивно:

dynamic foo = SomeCollection.Where
     (
          c => c.SomeProperty < (SomeValue * 2)
     )
     .Select
     (
          c => new {c.SomeProperty, c.OtherProperty}
     )

(пример из википедии Linq)

Маккей
источник
2
У вас есть опечатка: операторы linq являются декларативными, а не непоследовательными (у вас "C # функции более императивны, как Linq" следует читать декларативно.
Рид Копси
Исправлено (некоторое время назад)
McKay
8

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

С http://en.wikipedia.org/wiki/Declarative_programming

Короче говоря, декларативный язык проще, потому что ему не хватает сложности потока управления (циклы, операторы if и т. д.)

Хорошее сравнение - модель «code-behind» ASP.Net. У вас есть декларативные файлы «.ASPX», а затем императивные файлы кода «ASPX.CS». Я часто обнаруживаю, что если я смогу сделать все, что мне нужно, в декларативной половине сценария, гораздо больше людей смогут следить за тем, что делается.

Kervin
источник
7

Кража у Филиппа Робертса здесь :

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

Два примера:

1. Удвоение всех чисел в массиве

Обязательно следует:

var numbers = [1,2,3,4,5]
var doubled = []

for(var i = 0; i < numbers.length; i++) {
  var newNumber = numbers[i] * 2
  doubled.push(newNumber)
}
console.log(doubled) //=> [2,4,6,8,10]

Декларативно:

var numbers = [1,2,3,4,5]

var doubled = numbers.map(function(n) {
  return n * 2
})
console.log(doubled) //=> [2,4,6,8,10]

2. Суммирование всех элементов в списке

настоятельно

var numbers = [1,2,3,4,5]
var total = 0

for(var i = 0; i < numbers.length; i++) {
  total += numbers[i]
}
console.log(total) //=> 15

Декларативно

var numbers = [1,2,3,4,5]

var total = numbers.reduce(function(sum, n) {
  return sum + n
});
console.log(total) //=> 15

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

Крис Ивс-Колбреннер
источник
5
Как и в случае с пугающе многими ответами на этот вопрос, ваш пример «декларативного» программирования является примером функционального программирования. Семантика «map» состоит в том, чтобы «применять эту функцию к элементам массива по порядку». Вы не позволяете среде исполнения каких-либо ограничений в порядке исполнения.
Пит Киркхэм,
4

Императивное программирование
Язык программирования, требующий такой дисциплины, как C / C ++, Java, COBOL, FORTRAN, Perl и JavaScript. Программисты, пишущие на таких языках, должны разработать правильный порядок действий для решения проблемы, основанный на знании обработки данных и программирования.

Декларативное программирование
Компьютерный язык, который не требует написания традиционной логики программирования; Пользователи концентрируются на определении ввода и вывода, а не шагов программы, требуемых в процедурном языке программирования, таком как C ++ или Java.

Примеры декларативного программирования: CSS, HTML, XML, XSLT, RegX.

Premraj
источник
2

декларативная программа - это просто данные для ее более или менее «универсальной» императивной реализации / vm.

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

Минусы: ты угадаешь правильно. реализация универсального (и параметризованного DSL) императивного алгоритма / виртуальной машины может быть медленнее и / или потреблять больше памяти, чем конкретная. в некоторых случаях. если такие случаи редки - просто забудь об этом, пусть будет медленно. если это часто встречается - вы всегда можете расширить свой DSL / vm для этого случая. где-то тормозит все остальные случаи, конечно ...

PS Frameworks находится на полпути между DSL и императивом. и как все промежуточные решения ... они сочетают в себе недостатки, а не выгоды. они не такие безопасные и не такие быстрые :) посмотрите на хаскель мастера на все руки - он на полпути между сильным простым ML и гибким метапрограммой Пролог и ... каким монстром он является. Вы можете смотреть на Пролог как на Haskell с булевыми функциями / предикатами. и как просто его гибкость против Haskell ...

4KCheshireCat
источник
2

Мне просто интересно, почему никто не упомянул классы атрибутов как инструмент декларативного программирования в C #. Популярный ответ на этой странице только что говорил о LINQ как инструменте декларативного программирования.

Согласно Википедии

К распространенным декларативным языкам относятся языки запросов к базе данных (например, SQL, XQuery), регулярные выражения, логическое программирование, функциональное программирование и системы управления конфигурациями.

Таким образом, LINQ, как функциональный синтаксис, безусловно, является декларативным методом, но классы атрибутов в C #, как инструмент конфигурации, также декларативны. Вот хорошая отправная точка, чтобы узнать больше об этом: Краткий обзор программирования атрибутов C #

Сиаваш Мортазави
источник
Это хороший момент и более очевидный пример, я думаю. Такие вещи, как linq, все еще могут показаться императивными, так что это сбивает с толку тех, кто не знает различий, но атрибуты трудно увидеть иначе, чем декларативные. «теги» на членов, чтобы объявить, что вы хотите сделать с ними, но вы ни в коем случае не говорите «как». Я могу видеть, как кто-то может сказать, что запрос linq по-прежнему напоминает «как», потому что на каком-то уровне вы описываете форма логики, которая не такая тяжелая, но с атрибутами вы вообще не описываете логику. Вы просто помечаете вещи
user441521
2

Просто чтобы добавить еще один пример с точки зрения разработки мобильных приложений. В iOS и Android у нас есть Interface Builders, где мы можем определить интерфейс приложений.

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

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

Кроме того, некоторые новые языки, такие как Angular JS, фокусируются на декларативном проектировании пользовательских интерфейсов, и мы можем увидеть множество других языков, предлагающих такую ​​же поддержку. Например, у Java нет хорошего декларативного способа рисовать нативные настольные приложения в Java Swing или Java FX, но в ближайшем будущем они могут.

Пунит Шарма
источник
Ява не аббревиатура BTW
Mordechai
1

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

public static void main(String[] args)
{
    System.out.print("4 = 4.");
}

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

private static Scanner input;    

public static void main(String[] args) 
{
    input = new Scanner(System.in);
    System.out.println();
    System.out.print("Enter an integer value for x: ");
    int x = input.nextInt();
    System.out.print("Enter an integer value for y: ");        
    int y = input.nextInt();

    System.out.println();
    System.out.printf("%d == %d? %s\n", x, y, x == y);
}

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

Мэтью Конрад
источник