Elm — Как изменить параметризацию одного сигнала на основе другого сигнала

#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 .

Я думаю, что здесь у вас есть три варианта:

  1. Попросите добавить эту функцию в библиотеку времени в списке рассылки. (Инструкции по запросу функции немного раздуты для запроса такой функции, поэтому вы можете пропустить вещи, которые не применимы)
  2. Решите проблему либо изнутри Elm, либо в JavaScript, используя порты для подключения к Elm.
  3. Найдите способ не нуждаться в динамически изменяющемся Time сигнале.

Я советую вариант 1. Вариант 3 печален, вы должны быть в состоянии выполнить то, что вы просили в Elm. Вариант 2, возможно, не очень хорошая идея, если вы новичок в Elm. Вариант 1 не требует много работы, и люди в списке рассылки не клюют 😉

Чтобы подробнее остановиться на варианте 2, если вы хотите пойти на это:

  1. Если вы укажете исходящий порт для Signal Int и входящий порт для Signal Time , вы можете написать свою собственную функцию динамического времени на JavaScript. Смотрите http://elm-lang.org/learn/Ports.elm
  2. Если вы хотите сделать это из 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 вместо вашего, поскольку он лучше отвечает на мой вопрос; Я не ожидал больше ответов после вашего. Мне действительно понравилась та статья, на которую вы ссылались. Спасибо!