Визуализация Аттрактора Лоренца с помощью функционального программирования


Я работаю на этой визуализации Аттрактор Лоренца за прошедшие сутки. В настоящее время я также пытался изменить свой стиль кодирования в более функциональном программировании одной.

Я найти его довольно трудно, если честно, особенно "использовать только чистые функции." правило. Это в основном означает отсутствие побочных эффектов и функций, которые выполняют 1 задание. "Нет побочных эффектов". правило это, конечно же, невозможно при рисовании на HTML-холст. Поэтому я ищу оптимизация производительности и стиля советы код по ФП в целом.

Я также хотел бы, чтобы пройти 3 глобальных переменных (х, у, Z) в качестве параметров при вызове функции Update ("без глобальных переменных".) однако мне не повезло до сих пор. Я не могу найти способ, чтобы держать их сброса при использовании параметров. В отличие от функцию Draw, которая называет себя. Любая помощь приветствуется, спасибо за ваше время.

https://jsfiddle.net/9r8qcgnh/3/

{
  const canvas = document.querySelector('#canvas')
  const ctx = canvas.getContext('2d')
  ctx.fillStyle = 'rgb(255, 255, 255)'
  ctx.translate(500, 400)
  ctx.scale(10.5, 10.5)

  let x = 0.01
  let y = 0.0
  let z = 0.0

  function update () {
    const a = 10.0
    const b = 28.0
    const c = 8.0 / 3.0
    const dt = 0.015
    const dx = a * (y - x) * dt
    const dy = (x * (b - z) - y) * dt
    const dz = (x * y - c * z) * dt
    x = x + dx
    y = y + dy
    z = z + dz
    return { x: x, y: y, z: z }
  }

  function draw (hue) {
    const { x, y } = update()
    hue = hue || 0
    hue >= 240 ? (hue = 0) : hue++
    ctx.fillStyle = `hsl(${hue},100%, 50%)`
    ctx.fillRect(x, y, 0.1, 0.1)
    setTimeout(() => {
      return draw(hue)
    }, 5)
  }

  draw()
}


Комментарии
2 ответа

Я ничего не знаю о аттракторов Лоренца или функциональное программирование, но у меня есть несколько вещей, которые я хочу упомянуть.

getElementById это быстрее, чем querySelector

const canvas = document.getElementById('canvas')

Вы можете использовать сокращенного наименования в ничью функции.

return { x, y, z };

Вы можете дать параметры по умолчанию

function draw (hue = 0) {
//...
}

и удалите эту строку

hue = hue || 0

Я не знаю, если это преднамеренно, потому что вы что-то имеете против фиолетовый, но оттенок идет на 360.

Использовать точку с запятой в конце строки. Технически, JavaScript может работать и без них, но это просто... выглядит неправильно. Если что-то, сделать это, чтобы сделать его привычкой, так как это может стать проблемой, если вы что-то код на другом языке позже.

Представляется более целесообразным использовать setInterval вместо setTimeout здесь. Вам придется обрабатывать оттенок по-разному в этом случае. Делая это глобальный вариант, но не хороший, если вы пытаетесь избежать. Еще одним вариантом может стать генератор.

function draw (hue = 0) {
const { x, y } = update();
ctx.fillStyle = `hsl(${hue},100%, 50%)`;
ctx.fillRect(x, y, 0.1, 0.1);
}

const hueGenerator = (function*() {
let hue = 0;
while(true) {
hue %= 360;
yield hue++;
}
})();

setInterval(() => {
draw(hueGenerator.next().value);
}, 5)

2
ответ дан 1 марта 2018 в 11:03 Источник Поделиться


"Нет побочных эффектов". правило это, конечно же, невозможно при рисовании на HTML-холст.

Государства и побочных эффектов являются двумя вещами ФП избегает, но эффективно, что определяет программа. Мне нравится говорить людям, что государство и побочные эффекты всегда будут существовать, но "просто оттолкнули куда-нибудь в код/язык". Если вы их оттолкнуть, что вы останетесь с несколько вещей, которые могут быть написаны чисто. Например:

function update () {
const a = 10.0
const b = 28.0
const c = 8.0 / 3.0
const dt = 0.015
const dx = a * (y - x) * dt
const dy = (x * (b - z) - y) * dt
const dz = (x * y - c * z) * dt
x = x + dx
y = y + dy
z = z + dz
return { x: x, y: y, z: z }
}

a, b, c и dt все константы. Вы можете вытащить их из функции, они не должны быть переопределены при каждом обновлении. Вы можете либо определить их на улицу (в полном порядке, пока их значения не меняются), или создать замыкание (функция, которая возвращает другую функцию).

Вам не нужно обновлять x, yни z в этой функции просто возвращают их. Нажимаем побочных эффектов пока не надо. Акцент на генерации новых значений из старых ценностей.

hue = hue || 0
hue >= 240 ? (hue = 0) : hue++

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

setTimeout(() => {
return draw(hue)
}, 5)

Использовать requetAnimationFrame вместо. Это имеет много преимуществ по сравнению с таймерами, как позволяет плавно анимировать в 60фпс (или как там тебя частота обновления экрана).

Вот моя попытка сделать все ФП-иш. Все, что может быть написано чисто вырываются в свои функции. Сторона-effecty операции выталкиваются в их функции. Обратите внимание, что нет varS или letS или переуступок.



{
// Reference to our canvas
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

// Function accepts a, b, c and dt, and returns a function that accepts x, y and z
// which you can call to generate new x, y and z.
const getCoordsFactory = (a, b, c, dt) => (x, y, z) => ({
x: x + (a * (y - x) * dt),
y: y + (x * (b - z) - y) * dt,
z: z + (x * y - c * z) * dt
})

// Create a function with the given initial values for a, b, c and dt.
const getCoords = getCoordsFactory(10.0, 28.0, 8.0 / 3.0, 0.015)

// getHue accepts a hue value and either increments by 1 or resets to 0.
const getHue = hue => hue >= 240 ? 0 : hue + 1

// Updater function. Accepts previous values, gets updated values, and
// "recursively" calls itself with the updated values. Your "state"
// is now args of a recursive call.
const loop = (x0, y0, z0, hue0) => {
// Get updated values
const { x, y, z } = getCoords(x0, y0, z0)
const hue = getHue(hue0)

draw(x, y, z, hue)

// Schedule next draw. Looks recursive, but it's actually async.
requestAnimationFrame(loop.bind(null, x, y, z, hue))
}

// All the side-effecty operations pushed into 2 functions.

const draw = (x, y, z, hue) => {
ctx.fillStyle = `hsl(${hue},100%, 50%)`
ctx.fillRect(x, y, 0.1, 0.1)
}

const reset = () => {
ctx.fillStyle = 'rgb(255, 255, 255)'
ctx.translate(500, 400)
ctx.scale(10.5, 10.5)
}

reset()
loop(0.01, 0.0, 0.0, 0)
}


body {
margin: 0;
}

canvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #000;
}


<canvas id="canvas" width="1000" height="800"></canvas>



1
ответ дан 3 марта 2018 в 02:03 Источник Поделиться