Haskell,如何计算树的所有节点(模式匹配)

时间:2016-07-21 11:01:55

标签: haskell pattern-matching

问题在标题中:)

我的代码:

data tree a = Leaf a | Node (tree a) (tree a)

treeo = Node((Node(Leaf 1)(Node (Leaf 10)(Leaf 11))))
                (Node(Leaf 12)(Leaf 13))

-- count all leafs: 
lcounter(Leaf a) = 1
lcounter(Node a b)= lcounter a + lcounter b
-- count all nodes?:

2 个答案:

答案 0 :(得分:6)

首先注意数据类型定义应该从大写开始:

data Tree a = Leaf a | Node (Tree a) (Tree a)

要统计所有节点,它几乎就是你所做的,一个简单的模式匹配树数据构造函数:

countNodes :: Tree a -> Int
countNodes (Leaf a) = 0
countNodes (Node left right) = 1 + countNodes left + countNodes right

用你的例子:

let tree = Node((Node (Leaf 1) (Node (Leaf 10) (Leaf 11))))(Node (Leaf 12) (Leaf 13))
countNodes tree -- 4

答案 1 :(得分:3)

让机器完成工作。两个计数函数都是一个更通用概念的实例,折叠。我将编写少量代码来折叠树,并使用它来免费定义所需的两个计数函数。

我首先稍微概括一下您的Tree类型,在b s内部标记为Node s,并在a处外部标记为Leaf s data Tree a b = Leaf a | Node (Tree a b) b (Tree a b) 秒。

Tree

您的原始Tree a ()类型相当于Tree

Bifoldable版本是名为Bifoldable的有用类的实例。 instance Bifoldable Tree where bifoldMap f g (Leaf a) = f a bifoldMap f g (Node l x r) = bifoldMap f g l `mappend` g x `mappend` bifoldMap f g r 通用Foldable来处理具有两个类型参数的数据类型。

bifoldMap :: Monoid m => (a -> m) -> (b -> m) -> Tree a b -> m

Tree使用Monoida折叠为单个值。它会标识树中的所有bf,对其应用gm以获取mappend,然后使用countLeaves将结果结构压缩成单个值。

这是我要编写的唯一非常重要的代码。其他一切只是操纵类型; countNodesbifoldMap都会被Bifoldable的牙膏管挤压。 (有一些Template Haskell helpers可以为您生成Bifoldable的实例,因此您甚至不需要编写能够完成这项工作的代码!)

每个Foldable在两个方面自动为BifoldableThe WrappedBifunctor newtypeFoldable转换为newtype WrappedBifunctor p a b = WrapBifunctor { unwrapBifunctor :: p a b } instance Bifoldable p => Foldable (WrappedBifunctor p a) where foldMap f = bifoldMap (const mempty) f . unwrapBifunctor 聚合其第二个参数。

b

这已足以为我们提供一种计算节点的方法。计算节点与计算length类型参数的出现次数相同,这正是Foldable对任何Foldable所做的事情。我们所要做的就是将树包裹起来以获得-- i lied, this is nontrivial too - but it's already in the standard library length :: Foldable t => t a -> Int length = foldl' (\c _ -> c+1) 0 countNodes :: Tree a b -> Int countNodes = length . WrapBifunctor ghci> let myTree = Node (Node (Leaf 'a') () (Leaf 'b')) () (Node (Leaf 'c') () (Leaf 'd')) ghci> countNodes myTree 3

a

如何计算叶子?这次我们需要计算WrappedBifunctor类型参数的出现次数。 Tree也可以在这里提供帮助:我们可以重新排列a的参数,以便Foldable是第二个参数,然后将其包装起来 - 生成的a实例将计算b而不是flip s。为此,我们需要the Flip newtype,它会切换参数的参数,就像newtype Flip p a b = Flip { runFlip :: p b a } instance Bifoldable p => Bifoldable (Flip p) where bifoldMap f g = bifoldMap g f . runFlip 在值级别那样:

countLeaves :: Tree a b -> Int
countLeaves = length . WrapBifunctor . Flip

ghci> countLeaves myTree
4

现在我们可以免费计算叶子了:

Bifoldable

最后,您可以使用bilength :: Bifoldable t => t a b -> Int bilength = bifoldl' (\c _ -> c+1) (\c _ -> c+1) 0 countLeavesAndNodes :: Tree a b -> Int countLeavesAndNodes = bilength -- equivalent to, but more efficient than... -- countLeavesAndNodes t = countLeaves t + countNodes t 类中的bilength函数计算叶节点。

Flip

Haskell的理念是一劳永逸地解决这样的问题,并将这些一般解决方案应用于特定案例。像WrappedBifunctorBifunctor这样的新类型让我们表达了一般属性(例如"每个Bifunctor仍然是a之后你翻转它的参数")没有运行时成本。我们使用这些属性来操作类型类系统,编写代码来为我们计算bBifoldable

当你继续学习Haskell时,当程序是一般模式的一个实例时,你会发现它。设置上面的NSArray *sectionArray; int sectionCount=0; NSDictionary *orderedData; NSString *checkInStr, *checkOutStr; NSString *govtTaxes, *enhancementTotal, *grandTotal; - (void)viewDidLoad { [super viewDidLoad]; [self setupTable]; [self.bookingsTableView reloadData]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } -(void)viewDidDisappear:(BOOL)animated { if(doesSendNotification){ NSLog(@"summary view disappeared"); [[NSNotificationCenter defaultCenter] postNotificationName:@"SummaryViewDismissedNotification" object:self]; } } -(void)viewWillAppear:(BOOL)animated { [self.bookingsTableView reloadData]; } -(void)setupTable { self.bookingsTableView.rowHeight = UITableViewAutomaticDimension; self.bookingsTableView.estimatedRowHeight = 50.0; sectionArray = [[SummaryModel sharedInstance] getTableSections:self.s_sendEnhancementServerDict]; orderedData = [[SummaryModel sharedInstance] getOrderedData:self.s_sendEnhancementServerDict]; [self.bookingsTableView reloadData]; } #pragma mark- UITableview delegate and datasource methods -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ if(section==0){ return 3; } else if (section>0 && section<(sectionCount-1)){ int rows=(int)[[orderedData objectForKey:(NSString*)[sectionArray objectAtIndex:section]] count]; return rows; } else { return 4; } } -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return (NSString*)[sectionArray objectAtIndex:section]; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellIdentifier; UITableViewCell *cell; // UITableView *table = (UITableView*)[self.view viewWithTag:11]; if (indexPath.section==0 && indexPath.row>=0 && indexPath.row<=2) { cellIdentifier =@"SplitCell"; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } UILabel *l1 = (UILabel*)[cell viewWithTag:1]; UILabel *l2 = (UILabel*)[cell viewWithTag:2]; if(indexPath.row==0){ l1.attributedText = [self getStyledString1:@"Hotel Name"]; l2.attributedText = [self getStyledString:self.s_propertyName]; } else if(indexPath.row==1){ l1.attributedText = [self getStyledString1:@"Arrival Date:"]; l2.attributedText = [self getStyledString:checkInStr]; } else if(indexPath.row==2){ l1.attributedText = [self getStyledString1:@"Departure Date:"]; l2.attributedText = [self getStyledString:checkOutStr]; } } else if (indexPath.section>0 && indexPath.section<(sectionCount-1)) { // for(int i=0;i<5;i++){ cellIdentifier=@"VerticalLabelCell"; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } UILabel *l3 = (UILabel*)[cell viewWithTag:3]; UILabel *l4 = (UILabel*)[cell viewWithTag:4]; l3.layer.backgroundColor = GOLDEN_COLOR.CGColor; NSArray *roomTypeArray = [orderedData objectForKey:(NSString*)[sectionArray objectAtIndex:indexPath.section]]; NSDictionary *roomD = [roomTypeArray objectAtIndex:indexPath.row]; NSString *header = [roomD objectForKey:@"room_type_name"]; NSAttributedString *sH = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@",header] attributes:@{NSFontAttributeName:ARIAL_FONT_BOLD}]; l3.attributedText = sH; int roomCount = [(NSNumber*)[roomD objectForKey:@"room_units"] intValue]; NSMutableAttributedString *labelText = [[NSMutableAttributedString alloc] init]; for(int i=0;i<roomCount;i++){ NSString *roomNo = [NSString stringWithFormat:@"\n Room # %d\n",i+1]; NSAttributedString *s = [[NSAttributedString alloc] initWithString:roomNo attributes:@{NSFontAttributeName:ARIAL_FONT_BOLD, NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle)}]; [labelText appendAttributedString:s]; NSString *adults = [NSString stringWithFormat:@" Adults: %@ \t\t Max. Adults: %@ \n",[roomD objectForKey:@"max_adults"],[roomD objectForKey:@"max_adults"]]; NSAttributedString *s1 = [[NSAttributedString alloc] initWithString:adults attributes:@{NSFontAttributeName:ARIAL_FONT_BOLD}]; [labelText appendAttributedString:s1]; NSArray *enhanc = [(NSArray*)[roomD objectForKey:@"room_features"] objectAtIndex:i]; for(int i=0;i<[enhanc count];i++){ [labelText appendAttributedString:[self getStyledString2:[NSString stringWithFormat:@" %@\n", [enhanc objectAtIndex:i]]]]; } l4.attributedText = labelText; } } else if(indexPath.section==(sectionCount-1)){ cellIdentifier =@"SplitCell"; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } UILabel *l1 = (UILabel*)[cell viewWithTag:1]; UILabel *l2 = (UILabel*)[cell viewWithTag:2]; if(indexPath.row==0){ l1.attributedText = [self getStyledString1:@"Room Charges:"]; l2.attributedText = [self getStyledString:[NSString stringWithFormat:@"£ %@", self.s_priceOfRooms]]; }else if(indexPath.row==1){ l1.attributedText = [self getStyledString1:@"Government Taxes:"]; l2.attributedText = [self getStyledString:[NSString stringWithFormat:@"£ %@", govtTaxes]]; }else if(indexPath.row==2){ l1.attributedText = [self getStyledString1:@"Enhancement Total:"]; l2.attributedText = [self getStyledString:[NSString stringWithFormat:@"£ %@", enhancementTotal]]; }else if(indexPath.row==3){ l1.attributedText = [self getStyledString1:@"Total Charges"]; l2.attributedText = [self getStyledString:[NSString stringWithFormat:@"£ %@", grandTotal]]; } } return cell; } -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { sectionCount = (int)[sectionArray count]; return sectionCount; } -(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { view.tintColor = GOLDEN_COLOR; } -(NSAttributedString*)getStyledString:(NSString*)input { NSAttributedString *str = [[NSAttributedString alloc] initWithString:input attributes:@{NSForegroundColorAttributeName:GOLDEN_COLOR, NSFontAttributeName:ARIAL_FONT}]; return str; } -(NSAttributedString*)getStyledString1:(NSString*)input { NSAttributedString *str = [[NSAttributedString alloc] initWithString:input attributes:@{NSFontAttributeName:ARIAL_FONT_BOLD}]; return str; } -(NSAttributedString*)getStyledString2:(NSString*)input { NSAttributedString *str = [[NSAttributedString alloc] initWithString:input attributes:@{NSFontAttributeName:ARIAL_FONT}]; return str; } 实例之类的常规结构很快就会在代码重用中带来好处。