F#狼山羊白菜

时间:2012-07-15 06:05:24

标签: f# river-crossing-puzzle

首先,我很抱歉问this question。但是“逃离Zurg”文章给了我很多帮助,我可以为Wolf Goat Cabbage问题编写自己的解决方案。我在下面提出我的代码。我要你告诉我

  1. 如果我的代码是以F#和函数式编程的真正精神编写的,那么
  2. 这是解决问题的最佳方案

    open System
    
    (* 
      The type direction determines which direction the human is present.
      Left means that Human is present on the left side of the bank.
      Right means human is present on the right side of the bank.
    *)
    type Direction =
      | Left
      | Right
    
    (*
      Master list of animals
    *)
    let Animals = ["Wolf"; "Goat"; "Cabbage"]
    
    let DeadlyCombinations = [["Wolf"; "Goat"];["Goat"; "Cabbage"];]
    
    let isMoveDeadly list1 list2 =
      List.exists (fun n -> n = list1) list2
    
    let rec MoveRight animals =
      match animals with
        | [] -> []
        | head::tail -> 
          if (isMoveDeadly tail DeadlyCombinations) then
            MoveRight tail @ [head]
          else
            Console.WriteLine("Going to move " + head)
            tail
    
    let ListDiff list1 list2 = List.filter (fun n -> List.forall (fun x -> x <> n) list1) list2
    
    let MoveLeft animals = 
      let RightList = ListDiff animals Animals 
      let ShouldTakeAnimal = isMoveDeadly RightList DeadlyCombinations
      if (ShouldTakeAnimal) then
        let x = List.head RightList
        Console.WriteLine("Going to move " + x + " back")
        [x]
      else
        Console.WriteLine("Farmer goes back alone")
        []
    
    let rec Solve direction animals =
        match animals with 
        | [] -> Console.WriteLine("Solved")
        | _ ->
            match direction with
            | Left -> Solve Right (MoveRight animals) 
            | Right -> Solve Left (animals @ (MoveLeft animals))
    
    [<EntryPoint>]
    let main args =
        Solve Left Animals
        0
    

1 个答案:

答案 0 :(得分:6)

代码看起来非常实用。我做了一些改变。首先,我使用 sets 来表示移动,还有一些小的建议......

表示。您使用列表表示致命组合,因此["Goat"; "Wolf"]["Wolf"; "Goat"]不同,如果您的算法按其他顺序生成移动,它不会将其视为致命的举动。您应该尝试找到不会发生这种情况的表示,因此我将更改表示形式以使用集合:

let DeadlyCombinations = [set ["Wolf"; "Goat"]; set ["Goat"; "Cabbage"];] 

isMoveDeadly函数中,您可以将移动转换为使用的集合(但也许最好更改代码以便在任何地方使用集合):

let move = set list1

不必要的泛化。除此之外,函数isMoveDeadly总是将DeadlyMoves作为第二个参数,因此我不会将其作为参数传递(这是不必要的泛化)和我写道:

let isMoveDeadly list = 
  let move = set list
  DeadlyCombinations |> List.exists (fun n -> n = move) 

效率提示。MoveRight函数中,您使用的list @ [element]模式非常低效。这意味着您需要复制整个list以将元素追加到末尾。使用element::list(较少复制)将元素添加到前端更有效,然后反转列表。如果你把致命的动作表示为一组,我想你甚至不需要反转列表,所以我写道:

let rec MoveRight animals = 
  match animals with 
    | [] -> [] 
    | head::tail ->  
      if (isMoveDeadly tail) then 
        head :: (MoveRight tail)
      else 
        Console.WriteLine("Going to move " + head) 
        tail 

表示(再次)。您实施了自己的ListDiff功能,以找出给定列表中没有的动物。这表明使用集合(而不是列表)确实是更好的表示。如果切换到集合,则可以使用内置函数Set.difference代替。