|
Навигация
|
Главная » Java Статистическое программирование на R: Часть 3. Повторное использование кода и объектное программирование (исходники)Источник: IBM developerWorks Россия Девид Мертц (David Mertz) Первые две статьи этой серии (часть 1, часть 2) рассматривали R в применении к "реальному миру". Мы исследовали различные возможности статистического анализа и графического отображения, используя большие наборы данных о температуре, которые собрал один из авторов этих статей. Как было упомянуто в предыдущих статьях, мы фактически обследовали лишь малую часть всей глубины и богатства статистических библиотек R.В этой статье я хочу отойти от дальнейшего статистического анализа как такового (в основном потому, что я сам не имею необходимых знаний в области статистики, чтобы выбрать наиболее подходящий метод; и мой соавтор Брэд Хантинг, и многие читатели знают намного больше в этой области). В дополнение к богатству статистических понятий, предложенных в первых двух статьях, я ознакомлю читателя с некоторыми тонкостями, лежащими в основе языка программирования R. Предыдущие статьи рассказывали о функционально-ориентированном программировании в R; я подозреваю, что многим читателям будут больше близки процедурные и объектно-ориентированные языки программирования. Кроме того, ранее мы рассматривали R применительно к конкретным задачам. В этой статье будет обсуждаться создание повторно используемых и модульных компонентов для разработки R. Назад к основамПеред рассмотрением объектной модели R давайте изучим и уясним объекты и функции R. Главное, что нужно запомнить об объектах R - это то, что "все - вектор". Даже объекты, которые на первый взгляд отличаются от векторов - матрицы, массивы, структуры данных и остальное - на самом деле векторы с дополнительными (измененными) атрибутами, которые "указывают" R обрабатывать их специальным образом.Массив (обозначаемый dim ) - это один из наиболее важных атрибутов, которые имеют (некоторые) вектора R. Функции matrix() , array() и dim() - просто удобные функции для установки размерности вектора. Система ООП (объектно-ориентированного программирования) R также уходит корнями к атрибутам объектных (или других) классов.Давайте рассмотрим объявление массива на примере кода в Листинге 1. Листинг 1. Создание векторов и установка размерности
Кратко говоря, существуют различные синтаксические приемы установки атрибута dim вектору - но фактически все эти синтаксические ухищрения делают одно и то же.Единственное, что может смутить в положении "все - вектор" - это построчные и постолбцовые операции, которые могут быть не так интуитивно понятны. Например, достаточно легко создать двумерный массив (матрицу) и поработать с одним столбцом или строкой: Листинг 2. Построчные операции над матрицей
Но если бы вы захотели создать вектор сумм по строкам, вам, возможно, захотелось бы сделать что-либо вроде следующего: Листинг 3. Неправильный способ выполнения построчных операций
Здесь можно было бы использовать цикл, но это идет вразрез с функциональными и векторно-ориентированными операциями R. Правильное решение - использовать функцию apply() :Листинг 4. Функция apply() для построчных операций
Бесконечная последовательностьИногда, из чисто практических соображений, полезно использовать такую конструкцию, как бесконечная числовая последовательность. Например, мой соавтор по предыдущей статье делал некий анализ с помощью метода интегрирования Монте-Карло, и для его задачи было полезно использовать бесконечно длинную последовательность случайных чисел. Необходимо понять, что бесконечная последовательность - это не просто возможность генерации нового числа в случае необходимости; это также необходимость иметь возможность обращаться к любому предшествующему элементу и получать его прежнее значение.Очевидно, нет ни компьютерного языка, ни компьютера, способного хранить бесконечную последовательность - все, что они могут хранить, - это "ленивые" ( lazy ) и неограниченные ( unbounded ) последовательности. Новые элементы добавляются к уже реализованному списку тогда и только тогда, когда это необходимо. Например, в Python можно выполнить такое создание спископодобных объектов методом .__getitem__() , который расширяет внутренние списки по необходимости. В Haskell "ленивые" последовательности встроены глубоко внутрь языка - в результате все последовательности являются "ленивыми". В моем руководстве по Haskell (Ресурсы) я использовал пример создания списка всех простых чисел:Листинг 5. Список простых чисел в Haskell, полученных с помощью решета Эратосфена
В том, что касается бесконечности, R ближе к Python, чем к Haskell. Вам необходимо явно создавать новые элементы в случае необходимости. Подождем до раздела ООП, где мы увидим, как работает скрытый механизм векторного индексирования; здесь еще не так все запутано. Листинг 6. Объявление вектора и способа его динамического расширения
Вероятно, предпочтительнее получать значение элемента через функцию-контейнер getRand() . Заметим, что вы совершенно свободны в использовании срезов, вычисляемых значений либо отдельных индексов:Листинг 7. Использование функции-контейнера в качестве посредника для бесконечного виртуального вектора
Если необходимо, перед использованием элементов можно создать достаточно большой вектор с помощью функции assure() :Листинг 8. Расширение вектора (при необходимости) перед работой с ним
Объектно-ориентированное программирование в RR полностью отвечает требованиям объектно-ориентированного программирования, но чтобы понять это, нужно вспомнить о том, что такое ООП. Пользователи таких языков, как Java и C++ и даже Python, Ruby или Smalltalk, могут иметь несколько ограниченное представление об объектно-ориентированном программировании. Не ошибочное, но ограниченное одной моделью.Принципы ООП в R в большей степени основываются на обобщенных функциях, чем на иерархиях классов. Эта концепция будет близка читателям, использующим CLOS Lisp или тем, кто читал мои дискуссии по множественной диспетчеризации в Python. К сожалению, подход R - это единичная диспетчеризация; в этом отношении он эквивалентен "традиционным" языкам, упомянутым ранее (C++, Java и другие). Необходимо заметить, хотя это и не будет обсуждаться подробно в данной статье, что наиболее новая версия R сопровождается пакетом methods , который определяет и использует так называемые "формальные методы". Применение этих формальных методов во многом накладывает некую дисциплину (и ограничения), знакомые по традиционным языкам ООП. В любом случае формальное ООП в R строится поверх "неформального ООП", о котором будет рассказано в этой статье. Насколько я знаю, пакет methods не имеет окончательного статуса, но некая слегка модифицированная версия этого пакета, по-видимому, будет сохранена в будущих версиях R.Необходимо помнить, что суть концепции ООП на самом деле не в наследовании, а в более общем принципе - решениях по диспетчеризации . Например, вызов obj.method() в традиционных языках ООП будет использовать порядок разрешения методов ( method resolution order - MRO) объекта для поиска "первого" класса-предка obj , который имеет метод .method() .Что такое "первый" - это более тонкий вопрос, чем кажется на первый взгляд. R принимает те же решения, но выворачивает идею наследования наизнанку - вместо набора классов , которые могут объявлять и аннулировать различные методы внутри себя, R порождает семейство обобщенных (generic) функций , которые имеют метки, указывающие тип объекта, которым они хотят оперировать. Обобщенные функцииВ качестве простого примера создадим обобщенную функциюwhoami() и несколько помеченных методов для диспетчеризации:Листинг 9. Создание обобщенной функции и помеченных методов
Ключевая идея состоит в том, что каждый объект в R может принадлежать нулю, одному или большему числу классов. MRO любого заданного объекта (относительно конкретного метода) - это просто вектор именованных классов (если они есть) в атрибуте его class . Например:Листинг 10. Назначение объектам меток членства в классе
Как и в традиционных наследующих языках, объект не обязан использовать один и тот же класс для всех вызываемых методов. Традиционно, если Child наследуется от Mom и Dad , объект типа Child может использовать .meth1() от Mom и .meth2() от Dad . Все это можно сделать и в R, но Mom и Dad ничего не значат - это просто имена:Листинг 11. Разрешение методов
Включение предковНеобходимость явного указания MRO объекта вместо неявного разрешения через синтаксис наследования может показаться ограничивающей. В действительности можно легко реализовать основанный на наследовании синтаксис MRO, используя минимальное количество функций-контейнеров. MRO, используемый в Листинге 11, вероятно, не лучший из всех возможных, но он демонстрирует идею:Листинг 12. Реализация основанного на наследовании MRO с использованием минимального количества функций-контейнеров
В действии код Листинга 12 выглядит следующим образом: Листинг 13. Объект с основанным на наследовании MRO
Если следовать традиционному подходу отношений класс/наследование, надо включать имя создаваемого класса (подобно Mom в аргументе classes ). Фактически, учитывая, что каждый класс - это нормальный объект, рассмотренная выше система ближе к прототипному ООП, чем к основанному на классах.Опять же, вся система обладает достаточной гибкостью для реализации всех вариантов. Можно при желании совершенно свободно отделить объекты классов от объектов экземпляров - можно различать классы по соглашениям именования (например, Mom в отличие от mom ), присваивая другие атрибуты (например, type может быть class или instance ; функция обработки должна проверять тип) или другими способами.Снова о бесконечном вектореТеперь, имея некоторые механизмы ООП, можно намного удобнее работать с бесконечным вектором, который был описан ранее. Наше первое решение вполне работоспособно, но лучше было бы иметь еще более органичный и прозрачный бесконечный вектор.Операторы в R - это просто сокращенный способ вызова функций; вы можете свободно дифференцировать поведение операторов на основе классов, так же, как и для вызовов других функций. Попутно исправим еще несколько недостатков первой системы:
Листинг 14. Объявление индексируемого бесконечного случайного вектора
Индексирование уже определено в R как обобщенная функция, так что для его настройки не нужно вызывать метод UseMethod() ; можно просто определить столько новых специализаций, сколько нужно. Аналогично, встроенная функция print() тоже является обобщенной. Ею можно воспользоваться так:Листинг 15. Печать бесконечного вектора
In action, the code produces the following: Листинг 16. Пример печати бесконечного вектора
ЗаключениеДля программирования функций общего назначения, объектов и классов в R нужно сделать шаг назад от подхода традиционных процедурных и объектно-ориентированных языков программирования. Первые две статьи демонстрировали примеры конкретного статистического анализа и не требовали отдельного обдумывания, но однажды, когда вам захочется создать повторно используемый код, придется понять концепцию обобщенных функций и "вывернутый наизнанку" объектно-ориентированный подход, на котором основано их применение (форма ООП "наизнанку" в действительности является более общей).Вся хитрость такого понимания ООП в том, чтобы мыслить в терминах "какой код вызван?" и "как сделан выбор?". Не привязывайтесь к синтаксису, который применяется в конкретных языках - C++, Objective C, Java, Ruby или Python; сконцентрируйтесь на концепции диспетчеризации. Статистическое программирование на R: Часть 3. Повторное использование кода и объектное программирование (исходники). "Доктор Веб": Хронология эпидемии троянца BackDoor.Flashback.39. SSL VPN - шаг вперед в технологии VPN сетей. Oracle Day: день изменений. Oracle Enterprise Manager 11g дополнено новыми средствами управления бизнес-транзакциями. Главная » Java |
© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено. |