代码之家  ›  专栏  ›  技术社区  ›  jonny

用Perl表示允许的状态转换图

  •  6
  • jonny  · 技术社区  · 14 年前

    当前检查由丑陋的if语句处理

    我想用过渡矩阵代替它:

    my %allowed_status_changes = (
        1 => (2,5),     
        2 => (1,2,3,4,5),
        3 => (4,2),     
        4 => (3,2),     
        5 => (),        
    );
    my $is_allowed_transition = 
        $submitted_status ~~ $allowed_status_changes {$original_status};
    
    if ($prerequestsites && !$is_allowed_transition) {
        return;
    }
    

    某些转换只能在附加条件下允许,因此我需要

    2 => ( 
        (target => 1)
        (target => 2, condition => $some_condition)
        (target => (3,4), condition => $other_condition), 
        (target => 5)
    ),
    

    (依我看太长了)

    如何解析它以检查是否允许转换?

    3 回复  |  直到 14 年前
        1
  •  2
  •   DVK    14 年前

    如果条件非常普遍(例如,几乎每个允许的转换都有它们),那么后一个结构就非常好,除了用“,”而不是“{}”表示hashref的语法错误之外。

    请注意,检查代码的可读性是非常清楚,虽然体积庞大,并不是很习惯。

    my %allowed_status_changes = (
        1 => [2,5],
        2 => [1,5,{targets=>[2], conditions=>[$some_condition]}
                 ,{targets=>[3,4], conditions=>[$other_condition, $more_cond]}]
        3 => [4,2],     
        4 => [3,2],
        5 => [],        
    );
    
    sub is_allowed_transition {
        my ($submitted_status, $original_status ) = @_;
        foreach my $alowed_status (@$allowed_status_changes{$original_status}) {
            return 1 if !ref $alowed_status && $alowed_status == $submitted_status;
            if (ref $alowed_status) {
                foreach my $target (@$alowed_status{targets}) {
                    foreach my $condition (@$alowed_status{conditions}) {
                        return 1 if check_condition($submitted_status
                                                  , $original_status, $condition);    
                    }
                }
            }
        }
        return 0;
    }
    
    if ($prerequestsites
      && !$is_allowed_transition($submitted_status, $original_status )) {
        return;
    }
    
        2
  •  1
  •   Kent Fredric    14 年前

    尽管我在很大程度上同意DVK,但我不得不说,一旦你开始深入研究散列数组的数组,你就达到了一个代码复杂度的水平,如果没有大量的头和错误旋转,就很难维护它。

    my $transitions = TransitionGraph->new(); 
    $transition->add( 1, { targets => [ 2, 5 ] }); 
    $transition->add( 2, { targets => [ 1, 5 ] });
    $transition->add( 2, { targets => [ 2 ],    conditions => [ $some_condition ] });
    $transition->add( 2, { targets => [ 3, 4 ], conditions => [ $other_condition, $more_cond ]}); 
    $transition->add( 3, { targets => [4,2] } );
    $transition->add( 4, { targets => [3,2] } );
    $transition->add( 5, { targets => [] } );
    
    if( $transition->allowed( 1 , 3 )){ 
    
    }
    

    类的实现取决于用户,但我会使用Moose。

    这样做的主要好处是封装了状态图的工作方式,因此您可以直接使用它,并担心该图的工作方式与其使用位置分离。

    在内部,它可以这样做,或类似的事情:

    sub add { 
       my ( $self , $input_state, $rules ) = @_;
       my $state;
       if ( $self->has_state( $input_state ) ) { 
           $state = $self->get_state( $input_state );
       } else { 
           $state = TransitionGraphState->new( source_id => $input_state );
           $self->add_state( $input_state, $state );
       }
       my $targets = delete $rules{targets};
       for my $target ( @$targets ) {
          $state->add_target( $target, $rules );
       }
       return $self;
    }
    
    sub allowed { 
        my ( $self, $from, $to )  = @_; 
    
        if ( not $self->has_state( $from ) ){ 
             croak "NO source state $from in transition graph";
        }
        my $state = $self->get_state( $from );
        return $state->allowed_to( $to );
    }
    

    这还有一个很酷的好处,即不需要一个特定的代码集来处理子节点,您可以使用它们自己的行为创建单独的实例,以防您希望对一个源状态进行不同的处理。

     $transition->add_state( 2, $some_other_class_wich_works_like_transitiongraphstate );
    

    希望这有帮助。

        3
  •  1
  •   frankc    14 年前

    第二种形式没有问题。你不能回避这样一个事实:你必须在某个地方对状态机进行编码。事实上,我认为将整个机器编码在一个这样的地方,更容易理解抽象层太多的东西,你需要在n个不同的地方寻找,才能理解机器的流程。