F# - пока проблемы с условием кортежа

StackOverflow https://stackoverflow.com/questions/161348

  •  03-07-2019
  •  | 
  •  

Вопрос

Учитывая следующее:

#light
//any function returning bool * 'a
let foo =
    let x = ref 10
    fun () ->
        x := !x - 1
        if !x <> 0 then
            (true, x)
        else
            (false, x)

while let (c,x) = foo() in c do print_any x;//can't access x, but would be convinent.

//this is how I want it to work, without all the typing
let rec loop f =
    match f() with
    | (true, x) ->
        print_any x
        loop f
    | (false, _) -> ()
loop foo

Как мне решить эту проблему?Или мне просто нужно пройти через преобразование «foo» в выражение последовательности?

Это было полезно?

Решение

Это одно из решений, но лично я считаю, что это злоупотребление функцией while.

#light
while 
   (let (c,x) = foo()
    if c then print_any !x
    c)
   do ()

Другие советы

Еще одно решение, которое, на мой взгляд, немного лучше.Он получает x из области действия условия while и помещает его в ссылку y, которая доступна в более высокой области видимости.Все еще не лучшее (функциональное) решение, но оно работает.

let y = ref 1
while (let (c,x) = foo()
       y := !x
       c)
       do printf "%i" !y

Я думаю, твой rec loop Решение работает лучше всего, поскольку оно является наиболее функциональным (избегает побочных эффектов, хотя foo использует состояние) и наиболее общим (оно работает со всеми функциями одновременно с foo).Это дольше, но если вы будете использовать больше функций, таких как foo, цикл будет более продуктивным, чем одно кратчайшее решение только для foo.

Я бы даже еще больше обобщил цикл и абстрагировал действие того, что вы хотите сделать со значением в «истинной» ситуации:

let loop f a = 
   let rec loop2() = 
      match f() with
      | (true, x) ->
         a x
         loop2()
      | (false, _) -> ()
   loop2()

loop foo print_any

Мне нравятся другие предложения о том, как использовать «foo», предполагая, что foo фиксирован.

На мой нос пахнет кодом «foo».Если разумно преобразовать «foo» в «bar» по примеру

let bar =    
    let x = ref 10    
    seq {
        x := !x - 1        
        while !x <> 0 do
            yield x
            x := !x - 1        
    }
bar |> Seq.iter print_any

тогда я бы сделал это, но «бар», хотя и несколько лучше, все равно кажется подозрительным.(В «bar» я сохранил тот странный аспект, что он возвращает «int ref», а не просто «int», как это было в «foo», но, надеюсь, этот аспект был непреднамеренным?)

Я думаю, что самое интересное в "foo" - это неявная информация, которая не очевидна из типа данных (вы можете продолжать вызывать ее до тех пор, пока логическая часть верна), что делает версию seq немного более привлекательный.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top