#elm #elm-signal
#elm #elm-сигнал
Вопрос:
Как я могу параметризовать один сигнал на основе другого сигнала? например, предположим, я хотел изменить частоту кадров в секунду на основе x-положения мыши. Типы:
Mouse.x : Signal Int
fps : number -> Signal Time
Как я мог бы заставить Elm понять что-то в соответствии с этим псевдокодом:
fps (Mouse.x) : Signal Time
Очевидно, lift
в этом случае не работает. Я думаю, что результат был бы Signal (Signal Time)
(но я все еще новичок в Elm).
Спасибо!
Ответ №1:
Преамбула
fps Mouse.x
Приводит к ошибке типа, что для fps требуется Int
, а не Signal Int
.
lift fps Mouse.x : Signal (Signal Int)
Здесь вы правы. Как упоминает Cheatx’s answers, вы не можете использовать эти «вложенные сигналы» в Elm.
Ответ на ваш вопрос
Похоже, вы просите что-то, чего еще нет в стандартных библиотеках. Если я правильно понимаю ваш вопрос, вам нужен сигнал времени (или кадров в секунду), для которого синхронизация может изменяться динамически. Что-то вроде:
dynamicFps : Signal Int -> Signal Time
Использование встроенных функций, подобных lift
, не дает вам возможности самостоятельно создать такую функцию из функции типа Int -> Signal Time
.
Я думаю, что здесь у вас есть три варианта:
- Попросите добавить эту функцию в библиотеку времени в списке рассылки. (Инструкции по запросу функции немного раздуты для запроса такой функции, поэтому вы можете пропустить вещи, которые не применимы)
- Решите проблему либо изнутри Elm, либо в JavaScript, используя порты для подключения к Elm.
- Найдите способ не нуждаться в динамически изменяющемся
Time
сигнале.
Я советую вариант 1. Вариант 3 печален, вы должны быть в состоянии выполнить то, что вы просили в Elm. Вариант 2, возможно, не очень хорошая идея, если вы новичок в Elm. Вариант 1 не требует много работы, и люди в списке рассылки не клюют 😉
Чтобы подробнее остановиться на варианте 2, если вы хотите пойти на это:
- Если вы укажете исходящий порт для
Signal Int
и входящий порт дляSignal Time
, вы можете написать свою собственную функцию динамического времени на JavaScript. Смотрите http://elm-lang.org/learn/Ports.elm -
Если вы хотите сделать это из Elm, потребуется более уродливый взлом:
dynamicFps frames = let start = (0,0) time = every millisecond -- this strains your program enormously input = (,) <~ frames ~ time step (frameTime,now) (oldDelta,old) = let delta = now - old in if (oldDelta,old) == (0,0) then (frameTime,now) -- this is to skip the (0,0) start else if delta * frameTime >= second then (delta,now) else (0,old) in dropIf ((==) 0) 0 <| fst <~ foldp step start input
По сути, вы запоминаете абсолютную временную метку, запрашиваете новое время как можно быстрее и проверяете, достаточно ли велико время между запомненным временем и моментом сейчас, чтобы соответствовать нужному таймфрейму. Если это так, вы отправляете эту временную дельту (fps дает временные дельты) и запоминаете теперь как новую временную метку. Поскольку foldp отправляет все, что нужно запомнить, вы получаете как новую дельту, так и новое время. Таким образом, используя
fst <~
, вы сохраняете только дельту. Но время ввода (вероятно) намного быстрее, чем требуемый таймфрейм, поэтому вы также получаете много(0,old)
отfoldp
. Вот почему существуетdropIf ((==) 0)
.
Комментарии:
1. Вау, я действительно ценю этот подробный ответ! Мне придется протестировать его, прежде чем я его исключу, но я беспокоюсь о снижении производительности, вызванном
every millisecond
. Вероятно, я скоро отправлю запрос на функцию. Спасибо!
Ответ №2:
Вложенные сигналы явно запрещены системой типов Elm [часть 3.2 этой статьи].
Насколько я понимаю FRP, вложенные сигналы полезны только тогда, когда предоставляется какая-то лесть (например, монадическая функция ‘join’). И эту операцию трудно реализовать без сохранения всей истории сигналов.
Комментарии:
1. Хотя это полностью достоверная информация, похоже, она не отвечает на вопрос OP.
2. @Apanatshka Единственный вопрос, который я увидел, был «Как я могу параметризовать один сигнал на основе другого сигнала?». Извините, я действительно плохо угадываю.
3. Поскольку OP принял ваш ответ, вы, должно быть, угадали правильно или, по крайней мере, предоставили достаточно полезной информации. Поскольку мое предположение о том, что оно не отвечает на вопрос, также было предположением, я удалю значение -1. РЕДАКТИРОВАТЬ: по-видимому, я больше не могу его удалить, извините.
4. @Apanatshka Это был сарказм. Я пытался указать, что вы угадали основную проблему и проголосовали против меня за то, что я этого не сделал.
5. Я принял ответ @ Apanatshka вместо вашего, поскольку он лучше отвечает на мой вопрос; Я не ожидал больше ответов после вашего. Мне действительно понравилась та статья, на которую вы ссылались. Спасибо!