لماذا تدخل كرة يامبا هذه في حلقة لا نهاية لها؟
-
04-10-2019 - |
سؤال
أحاول محاكاة كرة كذبة مع Yampa-Framework: بالنظر إلى موضع X ، الطول والسرعة ، يجب أن ترتد الكرة وفقًا لقواعد الجاذبية. تأخذ وظيفة الإشارة "حدثًا طرفًا" كمدخلات ، وهي الفكرة "عندما يتم تمييز الكرة ، يجب أن تتضاعف السرعة".
ترتد الكرة بشكل جيد ، ولكن في كل مرة يكون هناك حدث تحول ، تنتقل الوظيفة إلى حلقة لا نهاية لها. كنت أحسب أنني ربما أحتاج إلى إضافة تأخير (dswitch ، pre ، notyet؟) ، لكنني لا أعرف كيف. سيكون موضع تقدير أي مساعدة!
{-# LANGUAGE Arrows #-}
module Ball where
import FRP.Yampa
type Position = Double
type Velocity = Double
type Height = Double
data Ball = Ball {
height :: Height,
width :: Position,
vel :: Velocity
} deriving (Show)
type Tip = Event ()
fly :: Position -> (Height, Velocity) -> SF Tip (Ball, Event (Height,Velocity))
fly w0 (h0, v0) = proc tipEvent -> do
let tip = (tipEvent == Event ())
v <- (v0+) ^<< integral -< -10.0
h <- (h0+) ^<< integral -< v
returnA -< (Ball h w0 v,
if h < 0 then Event (0,(-v*0.6))
else if tip then Event (h, (v*2))
else NoEvent)
bounce w (h,v) = switch (fly w (h,v)) (bounce w)
runBounce w (h,v) = embed (bounce 10 (100.0, 10.0)) (deltaEncode 0.1 [NoEvent, NoEvent, NoEvent, Event (), NoEvent])
تحرير: تمكنت من تجنب الحلقة التي لا نهاية لها عن طريق تغذية العلم عند حدوث طرف ، لكن هذا لا يزال لا يشعر بالطريقة الصحيحة للقيام بذلك ...
fly :: Position -> (Height, Velocity, Bool) -> SF Tip (Ball, Event (Height,Velocity,Bool))
fly w0 (h0, v0, alreadyTipped) = proc tipEvent -> do
let tip = tipEvent == Event () && (not alreadyTipped)
v <- (v0+) ^<< integral -< -10.0
h <- (h0+) ^<< integral -< v
returnA -< (Ball h w0 v,
if h < 0 then Event (0,(-v*0.6), False)
else if tip then Event (h, (v*2), True)
else NoEvent)
bounce w (h,v,alreadyTipped) = switch (fly w (h,v,alreadyTipped)) (bounce w)
المحلول
بعد بضعة أيام من القرصنة ، أعتقد أنني وجدت الجواب. الحيلة هي الاستخدام notYet
لتأخير حدث التبديل إلى النقطة التالية ، بحيث يتم التبديل (وبالتالي الدعوة العودية إلى fly
) يحدث عندما يختفي الحدث "القديم". ال second
تأكيد الوظيفة من أن الجزء الثاني فقط من النتيجة (Ball, Event (..))
سيتم وضعه من خلال notYet
. هذا يزيل الحلقة التي لا نهاية لها ، ولكنها تغير أيضًا الدلالات: يحدث التبديل الآن "خطوة زمنية" واحدة في وقت لاحق ، وهذا بدوره يؤدي إلى سرعة مختلفة.
هذا الشيء في Yampa هو في الواقع لطيف للغاية ، للأسف لا يوجد الكثير من الوثائق التي يجب العثور عليها. ما زلت لا أستطيع معرفة ما pre
و iPre
الوظائف جيدة ، وأنا أظهر أنها يمكن استخدامها في سياق مماثل.
fly :: Position -> (Height, Velocity) -> SF Tip (Ball, Event (Height,Velocity))
fly w0 (h0, v0) = proc tipEvent -> do
let tip = tipEvent == Event ()
v <- (v0+) ^<< integral -< -10.0
h <- (h0+) ^<< integral -< v
returnA -< (Ball h w0 v,
if h < 0 then Event (0,-v*0.6)
else if tip then Event (h, v*2)
else NoEvent)
bounce w (h,v) = switch (fly w (h,v) >>> second notYet) (bounce w)