2015-11-28 2 views
1

Я хочу создать отфильтрованный Signal для положения мыши. Он должен обновляться, когда кнопка мыши недоступна, или мышь переходит с «не вниз» на «вниз».Отфильтровать сигнал по значению другого сигнала

Я придумал эту функцию. Он работает, но использование трех анонимных функций кажется неправильным. Есть ли идиоматический способ сделать это?

mouseDownPosition: Signal (Int, Int) 
mouseDownPosition = 
    Signal.map2 (\(x, y) isDown -> (x, y, isDown)) Mouse.position Mouse.isDown 
    |> Signal.filter (\(x, y, isDown) -> isDown) (0, 0, False) 
    |> Signal.map (\(x, y, isDown) -> (x, y)) 

ответ

2

функция библиотеки

Существует утилита библиотека сигналов называется signal-extra (полное раскрытие: я являюсь автором этой библиотеки), которая имеет функцию только для этого: keepWhen: Signal Bool -> a -> Signal a -> Signal a.

[...] фильтр, который сохраняет события из Signal a до тех пор, как Signal Bool верно.

Реализация

Если вам интересно, то implementation делает почти то же самое, как вы писали сами, но есть тонкая разница!

keepWhen : Signal Bool -> a -> Signal a -> Signal a 
keepWhen boolSig a aSig = 
    zip boolSig aSig 
    |> sampleOn aSig 
    |> keepIf fst (True, a) 
    |> map snd 

zip просто Signal.map2 (,), чтобы объединить два сигнала. keepIf является старым (ИМХО четким) именем для Signal.filter. Так что это все в целом то же самое. Но после скрепления двух сигналов вместе, есть sampleOn. Таким образом, результат фильтрации является сигналом, который обновляется только тогда, когда сигнал исходного значения aSig обновляется и не обновляется при обновлении boolSig.

Альтернативной семантика

Там находится другой фильтр называется sampleWhen: Signal Bool -> a -> Signal a -> Signal a, который имеет описание:

Комбинация Signal.sampleOn и keepWhen. Когда первый сигнал станет True, будет распространено самое последнее значение второго сигнала .

Этот вариант совпадает с кодом, который вы написали, но он не соответствует вашему описанию. «Его значение должно измениться только при нажатии кнопки мыши».

Теперь вам решать, какое поведение вы действительно искали :)

+0

Спасибо! Вы правы, мое описание было немного неточным (я его обновил). В настоящее время целесообразно обрабатывать обновления 'isDown' и' position', поэтому мое решение и 'sampleWhen' работают хорошо.В какой-то момент я буду обрабатывать 'isDown' обновления отдельно, поэтому я буду использовать' keepWhen' или добавить 'sampleOn' в свою функцию. – joews

1

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

mouseDownPosition: Signal (Int, Int) 
mouseDownPosition = 
    Signal.map2 (,) Mouse.position Mouse.isDown 
    |> Signal.filter snd ((0, 0), False) 
    |> Signal.map fst 
2

Мне нравится @joews решение лучше, но только ради альтернативы, вот еще одна версия:

mouseDownPosition: Signal (Int, Int) 
mouseDownPosition = 
    Signal.map2 (\p d -> if d then Just p else Nothing) Mouse.position Mouse.isDown 
    |> Signal.filterMap identity (0,0) 
Смежные вопросы