编写一个计算玫瑰树中元素数量的函数

Kaf*_*Law -1 haskell

编写一个计算玫瑰树中元素数量的函数。

我试过计算一​​棵玫瑰树中元素的数量。

data RoseTree a = RoseNode a [RoseTree a]
    deriving Show
things :: RoseTree String
things = 
    RoseNode "thing" [
        RoseNode "animal" [
            RoseNode "cat" [], RoseNode "dog" []
        ],

        RoseNode "metal" [
            RoseNode "alloy" [
                RoseNode "steel" [], RoseNode "bronze" []
            ],
            RoseNode "element" [
                RoseNode "gold" [], RoseNode "tin" [], RoseNode "iron" []
            ]
        ],

        RoseNode "fruit" [
            RoseNode "apple" [
                RoseNode "Granny Smith" [], RoseNode "Pink Lady" []
            ],
            RoseNode "banana" [],
            RoseNode "orange" []
        ],

        RoseNode "astronomical object" [
            RoseNode "Planet" [
                RoseNode "Earth" [], RoseNode "Mars" []
            ],
            RoseNode "Star" [
                RoseNode "The Sun" [], RoseNode "Sirius" []
            ],
            RoseNode "Galaxy" [
                RoseNode "Milky Way" []
            ]
        ]
    ] 
Run Code Online (Sandbox Code Playgroud)

它应该是27,但是它返回4。

编辑:这是我的尝试:

roseSize x = case x of
    RoseNode a [] -> 0
    RoseNode a (x:xs) -> 1 + roseSize (RoseNode a xs)
Run Code Online (Sandbox Code Playgroud)

Sim*_*ine 6

它应该是27,但是它返回4。

roseSize x = case x of
  RoseNode a [] -> 0
  RoseNode a (x:xs) -> 1 + roseSize (RoseNode a xs)
Run Code Online (Sandbox Code Playgroud)

因此,您无需递归计算子树。试一试(固定为:基本情况为1

roseSize (RoseNode _ []) = 1
roseSize (RoseNode a (t:ts)) = roseSize t + roseSize (RoseNode a ts)
Run Code Online (Sandbox Code Playgroud)

缺少的部分是roseSize t

否则,您将只对树的第一层进行递归调用。

如果您手动评估功能,这很明显,

roseSize things
  ~> roseSize (RoseNode "thing" [ animals, metals, fruits, astronomical_objects ]
  ~> 1 + roseSize (RoseNode "thing" [ metals, fruits, astronomical_objects ])
  ~> 1 + 1 + roseSize (RoseNode "thing" [ fruits, astronomical_objects ])
  ~> 1 + 1 + 1 + roseSize (RoseNode "thing" [ astronomical_objects ])
  ~> 1 + 1 + 1 + 1 + roseSize (RoseNode "thing" [])
  ~> 1 + 1 + 1 + 1 + 0
Run Code Online (Sandbox Code Playgroud)

roseSize t在功能体内,评估变为

roseSize things
  ~> roseSize (RoseNode "thing" [ animals, metals, fruits, astronomical_objects ]
  ~> roseSize animals
      + roseSize (RoseNode "thing" [ metals, fruits, astronomical_objects ])
  ~> roseSize (RoseNode "animal" [ cat, dog ])
      + roseSize (RoseNode "thing" [ metals, fruits, astronomical_objects ])
  ~> roseSize cat
      + roseSize (RoseNode "animal" [ dog ])
      + roseSize (RoseNode "thing" [ metals, fruits, astronomical_objects ])
  ~> 1 -- given the base case of 1 instead of 0
      + roseSize (RoseNode "animal" [ dog ])
      + roseSize (RoseNode "thing" [ metals, fruits, astronomical_objects ])
  ~> ...
Run Code Online (Sandbox Code Playgroud)

作为练习,使此函数显式递归是可以的。

但是您可能要考虑使用更通用的方法,或者使用像PF这样的高阶函数卡斯特罗(Castro)做到了,或者像这样Data.Tree的现有数据结构containers

import qualified Data.Tree as T
import           Data.Tree (Tree(..))

things :: Tree String
things = Node "thing" [ animals, metals, fruits, astronomical_objects ]
  where
    animals = Node "animal" (map pure [ "cat", "dog" ])
    metals = Node "metal" [ alloys, elements ]
    alloys = Node "alloy" (map pure [ "steel", "bronze" ])
    elements = Node "element" (map pure [ "gold", "tin", "iron" ])
    fruits = ...
    astronomical_objects = ...
Run Code Online (Sandbox Code Playgroud)

由于a Data.TreeFoldable,因此可以length在其上使用。

因此,没有必要定义自定义roseSize函数。


此时,您正在计算树中的节点,而不是树的叶子叶子是实际对象,而不是它们所属的类别。因此,您可能实际上对计数叶子感兴趣。

您可以通过创建一个查找叶子的函数来做到这一点:

leaves :: Tree a -> [a]
leaves (Node x []) = ... -- x is a leaf
leaves (Node _x ts) = ... -- _x isn't a leaf
Run Code Online (Sandbox Code Playgroud)

使用此模板,您不能轻松地使用显式递归,即在on上进行匹配Node x (t:ts)leaves在on上调用ts,因为非叶子案例最终以基本案例结束,使得用尽的类别显示为叶子。但是你可以使用高阶函数抽象出递归,例如concatmapconcatMapPrelude


使用库玫瑰树还具有其他优点,例如,一堆其他类型类实例(Applicative使您pure "foo"可以构造单例树/叶子)和漂亮的打印功能:

> putStrLn $ T.drawTree things
thing
|
+- animal
|  |
|  +- cat
|  |
|  `- dog
|
`- metal
   |
   +- alloy
   |  |
   |  +- steel
   |  |
   |  `- bronze
   |
   ...
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢这种递归,因为`roseSize(RoseNode a [t1,t2,t3])的计算结果为`1 + roseSize t1 + 1 + roseSize t2 + 1 + roseSize t3`,这看起来是错误的。毕竟这也许是正确的,但我无法说服自己。那些额外的1看起来很多余。 (2认同)
  • 我认为实际上是因为它应该是`roseSize(RoseNode _ [])= 1`。我认为这也会从第二种情况中删除“ 1+”,同时也解决@chi的查询。 (2认同)
  • 基本情况*必须*为1:此定义不允许有空树,因此`roseSize`永远不会返回0。但是,节点*不能*有子代,这意味着* sum *的值可以为0。 (2认同)