Данный пост практически не содержит ничего нового или того, что мы не делали раньше. Этот пост носит скорее агитационный характер и содержит весьма простой пример, про который мы рассказывали уже дважды. Команда блога настоятельно рекомендует ознакомится с данным постом всем, кого интересует дальнейшее распространение информации по D и кто обладает достаточным опытом для решения на нем задач; также к прочтению приглашаются все те, кому просто интересно.
Rosetta Code — это очень интересный и познавательный ресурс, названный в честь Розеттского камня, и который содержит на своих страницах множество примеров решения некоторых задач на разных языках программирования. Прежде всего этот ресурс — это свободная wiki, статьи в которой имеют несколько способов группировки: по задаче и по языку программирования, на котором сделано решение. Решения, как и сами задачи, очень разные — и это позволяет сопоставлять языки программирования, а также очень быстро найти качественную реализацию некоторого алгоритма или просто посмотреть любопытный пример кода. Сайт Rosetta Code служит неплохой хрестоматией, а иногда даже помогает разрешать проблемы, вот почему написание статей для этой wiki очень полезно, и мы также не остались в стороне.
В качестве задачи, мы выбрали задачу построения папоротника Барнсли— известного фрактала, который строится с помощью итерируемой системы функций, и который дает красивое изображение папоротника.
Примечание редактора. Папоротник Барнсли — фрактал, названный в честь Майкла Барнсли, британского математика, который первым описал его в своей книге «Фракталы Повсюду».
У нас уже было несколько различных вариантов построения этого фрактала, но ни один из них не подходил, т.к использовались GUI-библиотеки или же наши собственные надстройки. Также предыдущие наши реализации не проходили потому, что код был слишком большой и из-за этого утрачивалась наглядность. Именно поэтому решено было посмотреть текущие решения этой задачи на соответствующей странице Rosetta Code.
Вы удивитесь, но в качестве исходного материала была выбрана реализация на Perl 6:
use Image::PNG::Portable; my ($w, $h) = (640, 640); my $png = Image::PNG::Portable.new: :width($w), :height($h); my ($x, $y) = (0, 0); for ^2e5 { my $r = 100.rand; ($x, $y) = do given $r { when $r <= 1 { ( 0, 0.16 * $y ) } when $r <= 8 { ( 0.20 * $x - 0.26 * $y, 0.23 * $x + 0.22 * $y + 1.60) } when $r <= 15 { (-0.15 * $x + 0.28 * $y, 0.26 * $x + 0.24 * $y + 0.44) } default { ( 0.85 * $x + 0.04 * $y, -0.04 * $x + 0.85 * $y + 1.60) } }; $png.set(($w / 2 + $x * 60).Int, $h - ($y * 60).Int, 0, 255, 0); } $png.write: 'Barnsley-fern-perl6.png';
Для своей реализации папоротника мы выбрали в качестве графической библиотеки dlib, а саму реализацию решили построить в виде single-style скрипта:
#!/usr/bin/env dub /+ dub.sdl: dependency "dlib" version="~>0.21.0" +/ import std.random; import dlib.image; void main() { enum WIDTH = 640; enum HEIGHT = 640; enum ITERATIONS = 2E6; float x = 0.0f; float y = 0.0f; auto rng = Random(unpredictableSeed); auto color = Color4f(0.0f, 1.0f, 0.0f); auto img = image(WIDTH, HEIGHT); foreach (_; 0..ITERATIONS) { auto r = uniform(0, 101, rng); if (r <= 1) { x = 0.0; y = 0.16 * y; } else { if (r <= 8) { x = 0.20 * x - 0.26 * y; y = 0.23 * x + 0.22 * y + 1.60; } else { if (r <= 15) { x = -0.15 * x + 0.28 * y; y = 0.26 * x + 0.24 * y + 0.44; } else { x = 0.85 * x + 0.04 * y; y = -0.04 * x + 0.85 * y + 1.6; } } } auto X = cast(int) (WIDTH / 2.0 + x * 60); auto Y = HEIGHT - cast(int)(y * 60); img[X, Y] = color; } img.saveImage(`barnsley_dlib.png`); }
Результат выглядит так:

И хотя первоначальный вариант включал в себя использование ppmformats, для примера под Rosetta Code, мы выбрали dlib, поскольку посчитали, что ppmformats еще слишком мало известная библиотека (хотя и присутствует в реестре dub). Кроме того, первоначальный вариант вообще не требовал использование dub для сборки и собирался с помощью redo.
Сборочный рецепт для redo (предполагается, что исходный код для примера находится в файле barnsley.d, а библиотека в файле — ppmformats.d):
redo-ifchange barnsley.d ppmformats.d ldc2 -Oz -release barnsley.d ppmformats.d -of $3 strip -s $3 chmod +rx $3 ./$3 rm $3 echo $1
Исходный код примера:
import std.random; import ppmformats; void main() { enum WIDTH = 640; enum HEIGHT = 640; enum ITERATIONS = 2E6; float x = 0.0f; float y = 0.0f; auto rng = Random(unpredictableSeed); auto color = new RGBColor(0, 255, 0); auto img = image(WIDTH, HEIGHT); foreach (_; 0..ITERATIONS) { auto r = uniform(0, 101, rng); if (r <= 1) { x = 0.0; y = 0.16 * y; } else { if (r <= 8) { x = 0.20 * x - 0.26 * y; y = 0.23 * x + 0.22 * y + 1.60; } else { if (r <= 15) { x = -0.15 * x + 0.28 * y; y = 0.26 * x + 0.24 * y + 0.44; } else { x = 0.85 * x + 0.04 * y; y = -0.04 * x + 0.85 * y + 1.6; } } } auto X = cast(int) (WIDTH / 2.0 + x * 60); auto Y = HEIGHT - cast(int)(y * 60); img[X, Y] = color; } img.save(`barnsley_ppmformats.ppm`); }
Стоит сказать, что мы честно разместили пример на Rosetta Code, почти сразу после заимствования кода для рисования окружности оттуда. Более того, это был очень занимательный опыт и потому мы рекомендуем вам ознакомится со списком нерешенных задач и написать пример хотя бы для одной из них — это сильно поможет в развитии языка.
P.S: Долг отдан.