Вяз - Как изменить параметризацию одного сигнала на основе другого сигнала
Как я могу параметризовать один сигнал на основе другого сигнала? Например, предположим, что я хотел изменить fps в зависимости от положения мыши по оси x. Типы:
Mouse.x : Signal Int
fps : number -> Signal Time
Как я мог заставить Вяза понять что-то вроде этого псевдокода:
fps (Mouse.x) : Signal Time
Очевидно, что lift
не работает в этом случае. Я думаю, что результат будет Signal (Signal Time)
(но я все еще новичок в Вязов).
Спасибо!
2 ответа
преамбула
fps Mouse.x
В результате возникает ошибка типа, для которой fps требуется Int
не Signal Int
,
lift fps Mouse.x : Signal (Signal Int)
Вы правы там. Как упоминает ответ CheatX, вы не можете использовать эти "вложенные сигналы" в Elm.
Ответ на ваш вопрос
Похоже, вы просите что-то, что еще не существует в стандартных библиотеках. Если я правильно понимаю ваш вопрос, вы бы хотели сигнал времени (или fps), время которого можно динамически изменять. Что-то вроде:
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)
,
Вложенные сигналы явно запрещены системой типов Элма [часть 3.2 этой статьи].
Насколько я понимаю FRP, вложенные сигналы полезны только тогда, когда предусмотрена какая-то польщенность (например, монадическая функция 'join'). И эту операцию трудно реализовать без сохранения всей истории сигналов.