|
|
|||||||||||||||||||||||||||||
|
Статистическое программирование на R: Часть 3. Повторное использование кода и объектное программирование (исходники)Источник: IBM developerWorks Россия Девид Мертц (David Mertz)
Первые две статьи этой серии (часть 1, часть 2) рассматривали R в применении к "реальному миру". Мы исследовали различные возможности статистического анализа и графического отображения, используя большие наборы данных о температуре, которые собрал один из авторов этих статей. Как было упомянуто в предыдущих статьях, мы фактически обследовали лишь малую часть всей глубины и богатства статистических библиотек R. В этой статье я хочу отойти от дальнейшего статистического анализа как такового (в основном потому, что я сам не имею необходимых знаний в области статистики, чтобы выбрать наиболее подходящий метод; и мой соавтор Брэд Хантинг, и многие читатели знают намного больше в этой области). В дополнение к богатству статистических понятий, предложенных в первых двух статьях, я ознакомлю читателя с некоторыми тонкостями, лежащими в основе языка программирования R. Предыдущие статьи рассказывали о функционально-ориентированном программировании в R; я подозреваю, что многим читателям будут больше близки процедурные и объектно-ориентированные языки программирования. Кроме того, ранее мы рассматривали R применительно к конкретным задачам. В этой статье будет обсуждаться создание повторно используемых и модульных компонентов для разработки R. Назад к основамПеред рассмотрением объектной модели R давайте изучим и уясним объекты и функции R. Главное, что нужно запомнить об объектах R - это то, что "все - вектор". Даже объекты, которые на первый взгляд отличаются от векторов - матрицы, массивы, структуры данных и остальное - на самом деле векторы с дополнительными (измененными) атрибутами, которые "указывают" R обрабатывать их специальным образом. Массив (обозначаемый Давайте рассмотрим объявление массива на примере кода в Листинге 1. Листинг 1. Создание векторов и установка размерности
Кратко говоря, существуют различные синтаксические приемы установки атрибута Единственное, что может смутить в положении "все - вектор" - это построчные и постолбцовые операции, которые могут быть не так интуитивно понятны. Например, достаточно легко создать двумерный массив (матрицу) и поработать с одним столбцом или строкой: Листинг 2. Построчные операции над матрицей
Но если бы вы захотели создать вектор сумм по строкам, вам, возможно, захотелось бы сделать что-либо вроде следующего: Листинг 3. Неправильный способ выполнения построчных операций
Здесь можно было бы использовать цикл, но это идет вразрез с функциональными и векторно-ориентированными операциями R. Правильное решение - использовать функцию Листинг 4. Функция apply() для построчных операций
Бесконечная последовательностьИногда, из чисто практических соображений, полезно использовать такую конструкцию, как бесконечная числовая последовательность. Например, мой соавтор по предыдущей статье делал некий анализ с помощью метода интегрирования Монте-Карло, и для его задачи было полезно использовать бесконечно длинную последовательность случайных чисел. Необходимо понять, что бесконечная последовательность - это не просто возможность генерации нового числа в случае необходимости; это также необходимость иметь возможность обращаться к любому предшествующему элементу и получать его прежнее значение. Очевидно, нет ни компьютерного языка, ни компьютера, способного хранить бесконечную последовательность - все, что они могут хранить, - это "ленивые" ( lazy ) и неограниченные ( unbounded ) последовательности. Новые элементы добавляются к уже реализованному списку тогда и только тогда, когда это необходимо. Например, в Python можно выполнить такое создание спископодобных объектов методом Листинг 5. Список простых чисел в Haskell, полученных с помощью решета Эратосфена
В том, что касается бесконечности, R ближе к Python, чем к Haskell. Вам необходимо явно создавать новые элементы в случае необходимости. Подождем до раздела ООП, где мы увидим, как работает скрытый механизм векторного индексирования; здесь еще не так все запутано. Листинг 6. Объявление вектора и способа его динамического расширения
Вероятно, предпочтительнее получать значение элемента через функцию-контейнер
Если необходимо, перед использованием элементов можно создать достаточно большой вектор с помощью функции Листинг 8. Расширение вектора (при необходимости) перед работой с ним
Объектно-ориентированное программирование в RR полностью отвечает требованиям объектно-ориентированного программирования, но чтобы понять это, нужно вспомнить о том, что такое ООП. Пользователи таких языков, как Java и C++ и даже Python, Ruby или Smalltalk, могут иметь несколько ограниченное представление об объектно-ориентированном программировании. Не ошибочное, но ограниченное одной моделью. Принципы ООП в R в большей степени основываются на обобщенных функциях, чем на иерархиях классов. Эта концепция будет близка читателям, использующим CLOS Lisp или тем, кто читал мои дискуссии по множественной диспетчеризации в Python. К сожалению, подход R - это единичная диспетчеризация; в этом отношении он эквивалентен "традиционным" языкам, упомянутым ранее (C++, Java и другие). Необходимо заметить, хотя это и не будет обсуждаться подробно в данной статье, что наиболее новая версия R сопровождается пакетом Необходимо помнить, что суть концепции ООП на самом деле не в наследовании, а в более общем принципе - решениях по диспетчеризации . Например, вызов Что такое "первый" - это более тонкий вопрос, чем кажется на первый взгляд. R принимает те же решения, но выворачивает идею наследования наизнанку - вместо набора классов , которые могут объявлять и аннулировать различные методы внутри себя, R порождает семейство обобщенных (generic) функций , которые имеют метки, указывающие тип объекта, которым они хотят оперировать. Обобщенные функцииВ качестве простого примера создадим обобщенную функцию Листинг 9. Создание обобщенной функции и помеченных методов
Ключевая идея состоит в том, что каждый объект в R может принадлежать нулю, одному или большему числу классов. MRO любого заданного объекта (относительно конкретного метода) - это просто вектор именованных классов (если они есть) в атрибуте его Листинг 10. Назначение объектам меток членства в классе
Как и в традиционных наследующих языках, объект не обязан использовать один и тот же класс для всех вызываемых методов. Традиционно, если Листинг 11. Разрешение методов
Включение предковНеобходимость явного указания MRO объекта вместо неявного разрешения через синтаксис наследования может показаться ограничивающей. В действительности можно легко реализовать основанный на наследовании синтаксис MRO, используя минимальное количество функций-контейнеров. MRO, используемый в Листинге 11, вероятно, не лучший из всех возможных, но он демонстрирует идею:
В действии код Листинга 12 выглядит следующим образом: Листинг 13. Объект с основанным на наследовании MRO
Если следовать традиционному подходу отношений класс/наследование, надо включать имя создаваемого класса (подобно Опять же, вся система обладает достаточной гибкостью для реализации всех вариантов. Можно при желании совершенно свободно отделить объекты классов от объектов экземпляров - можно различать классы по соглашениям именования (например, Снова о бесконечном вектореТеперь, имея некоторые механизмы ООП, можно намного удобнее работать с бесконечным вектором, который был описан ранее. Наше первое решение вполне работоспособно, но лучше было бы иметь еще более органичный и прозрачный бесконечный вектор. Операторы в R - это просто сокращенный способ вызова функций; вы можете свободно дифференцировать поведение операторов на основе классов, так же, как и для вызовов других функций. Попутно исправим еще несколько недостатков первой системы:
Сделаем все это: Листинг 14. Объявление индексируемого бесконечного случайного вектора
Индексирование уже определено в R как обобщенная функция, так что для его настройки не нужно вызывать метод Листинг 15. Печать бесконечного вектора
In action, the code produces the following: Листинг 16. Пример печати бесконечного вектора
ЗаключениеДля программирования функций общего назначения, объектов и классов в R нужно сделать шаг назад от подхода традиционных процедурных и объектно-ориентированных языков программирования. Первые две статьи демонстрировали примеры конкретного статистического анализа и не требовали отдельного обдумывания, но однажды, когда вам захочется создать повторно используемый код, придется понять концепцию обобщенных функций и "вывернутый наизнанку" объектно-ориентированный подход, на котором основано их применение (форма ООП "наизнанку" в действительности является более общей). Вся хитрость такого понимания ООП в том, чтобы мыслить в терминах "какой код вызван?" и "как сделан выбор?". Не привязывайтесь к синтаксису, который применяется в конкретных языках - C++, Objective C, Java, Ruby или Python; сконцентрируйтесь на концепции диспетчеризации.
|
|