একটি বিমূর্ত সিনট্যাক্স গাছের জন্য দর্শনার্থী প্যাটার্ন কার্যকর করা


23

আমি আমার নিজস্ব প্রোগ্রামিং ভাষা তৈরির প্রক্রিয়াধীন, যা আমি শেখার উদ্দেশ্যে করি। আমি ইতিমধ্যে আমার ভাষার একটি উপসেটের জন্য লেক্সার এবং একটি পুনরাবৃত্ত ডিসেন্ট পার্সার লিখেছি (আমি বর্তমানে গাণিতিক এক্সপ্রেশন যেমন, + - * /এবং প্রথম বন্ধনী সমর্থন করি )। পার্সার আমাকে একটি বিমূর্ত সিনট্যাক্স ট্রি ফিরিয়ে দেয়, যার উপরে আমি Evaluateঅভিব্যক্তির ফলাফল পাওয়ার জন্য পদ্ধতিটি কল করি । সবকিছু ঠিকঠাক কাজ করে। এখানে প্রায় আমার বর্তমান পরিস্থিতি রয়েছে (সি # তে কোডের উদাহরণ, যদিও এটি ভাষা-অজ্ঞাতসৃষ্টিকর হলেও):

public abstract class Node
{
    public abstract Double Evaluate();
}

public class OperationNode : Node
{
    public Node Left { get; set; }
    private String Operator { get; set; }
    private Node Right { get; set; }

    public Double Evaluate()
    {
        if (Operator == "+")
            return Left.Evaluate() + Right.Evaluate();

        //Same logic for the other operators
    }
}

public class NumberNode : Node
{
    public Double Value { get; set; }

    public Double Evaluate()
    {
        return Value;
    }
}

তবে আমি গাছের নোডগুলি থেকে অ্যালগরিদমটি ডিকুয়াল করতে চাই কারণ আমি ওপেন / ক্লোজড নীতি প্রয়োগ করতে চাই তাই উদাহরণস্বরূপ কোড জেনারেশন প্রয়োগ করতে চাইলে আমাকে প্রতিটি নোড ক্লাসটি আবার খুলতে হবে না। আমি পড়েছি যে ভিজিটর প্যাটার্ন এটির জন্য ভাল। প্যাটার্নটি কীভাবে কাজ করে সে সম্পর্কে আমার ভাল ধারণা আছে এবং ডাবল প্রেরণের সাহায্যে যাওয়ার উপায়। তবে গাছটির পুনরাবৃত্তিমূলক প্রকৃতির কারণে আমার কীভাবে এটির কাছে যাওয়া উচিত তা আমি নিশ্চিত নই। আমার দর্শনার্থীর মত দেখতে এখানে:

public class AstEvaluationVisitor
{
    public void VisitOperation(OperationNode node)
    {
        // Here is where I operate on the operation node.
        // How do I implement this method?
        // OperationNode has two child nodes, which may have other children
        // How do I work the Visitor Pattern around a recursive structure?

        // Should I access children nodes here and call their Accept method so they get visited? 
        // Or should their Accept method be called from their parent's Accept?
    }

    // Other Visit implementation by Node type
}

সুতরাং এটি আমার সমস্যা। আমি তত্ক্ষণাত এটিকে মোকাবেলা করতে চাই যখন আমার ভাষা পরে আরও বড় সমস্যা এড়াতে প্রচুর কার্যকারিতা সমর্থন করে না।

আমি এটি স্ট্যাকওভারফ্লোতে পোস্ট করিনি কারণ আমি চাই না আপনি কোনও বাস্তবায়ন সরবরাহ করুন। আমি কেবল চাই আপনি ধারণাগুলি এবং ধারণাগুলি ভাগ করুন যা আমি মিস করেছি এবং এবং কীভাবে আমার এটির কাছে আসা উচিত।


1
আমি সম্ভবত পরিবর্তে একটি গাছ ভাঁজ বাস্তবায়ন করতাম
জে.কে।

@ জে কে: আপনি কি কিছুটা বিশদ বোধ করতে চান?
মার্কো ফিসেট

উত্তর:


10

চাইল্ড নোডগুলি এবং কোন অর্ডারে যেতে হবে তা সিদ্ধান্ত নেওয়া ভিজিটর বাস্তবায়নের উপর নির্ভর করে। এটি ভিজিটর প্যাটার্নের পুরো পয়েন্ট।

আরও পরিস্থিতির জন্য দর্শনার্থীকে অভিযোজিত করার জন্য এই জাতীয় জেনেরিকগুলি (এটি জাভা) ব্যবহার করা সহায়ক (এবং বেশ সাধারণ):

public interface ExpressionNodeVisitor<R, P> {
    R visitNumber(NumberNode number, P p);
    R visitBinary(BinaryNode expression, P p);
    // ...
}

এবং একটি acceptপদ্ধতি এর মত দেখতে হবে:

public interface ExpressionNode extends Node {
    <R, P> R accept(ExpressionNodeVisitor<R, P> visitor, P p);
    // ...
}

এটি দর্শকদের অতিরিক্ত পরামিতিগুলি পাস করতে এবং এটি থেকে ফলাফল পুনরুদ্ধার করতে দেয় allows সুতরাং, এক্সপ্রেশন মূল্যায়ন এইভাবে প্রয়োগ করা যেতে পারে:

public class EvaluatingVisitor
    implements ExpressionNodeVisitor<Double, Void> {
    public Double visitNumber(NumberNode number, Void p) {
        // Parse the number and return it.
        return Double.valueOf(number.getText());
    }
    public Double visitBinary(BinaryNode binary, Void p) {
        switch (binary.getOperator()) {
        case '+':
            return binary.getLeftOperand().accept(this, p)
                + binary.getRightOperand().accept(this, p);
        // More cases for other operators here.
        }
    }
}

acceptপদ্ধতি প্যারামিটার উপরোক্ত উদাহরণে ব্যবহার করা হয় না, কিন্তু শুধু আমাকে বিশ্বাস: এটা এক আছে বেশ দরকারী। উদাহরণস্বরূপ, এটিতে ত্রুটিগুলি প্রতিবেদন করা কোনও লগার উদাহরণ হতে পারে।


আমি অনুরূপ কিছু বাস্তবায়ন শেষ করেছি এবং আমি এ পর্যন্ত ফল নিয়ে খুব সন্তুষ্ট। ধন্যবাদ!
মার্কো ফিসেট

6

আমি পূর্বে একটি পুনরাবৃত্ত গাছের উপর দর্শকের প্যাটার্নটি প্রয়োগ করেছি।

আমার নির্দিষ্ট পুনরাবৃত্তির উপাত্ত কাঠামো অত্যন্ত সহজ ছিল - মাত্র তিনটি নোড প্রকার: জেনেরিক নোড, একটি অভ্যন্তরীণ নোড যার বাচ্চা রয়েছে এবং একটি পাত নোড যাতে ডেটা রয়েছে। আপনার এএসটি হওয়ার চেয়ে আমি এটি অনেক সহজ, তবে সম্ভবত ধারণাগুলি স্কেল করতে পারে।

আমার ক্ষেত্রে আমি ইচ্ছাকৃতভাবে বাচ্চাদের সাথে একটি নোডের গ্রহণকে তার বাচ্চাদের কাছে গ্রাহক কল করতে বা দর্শনার্থীকে কল করতে দেয়নি the নোডের শিশুদের পরিদর্শন করা গৃহীত গ্রহণগুলির জন্য এটি "ভিজিট" সদস্য বাস্তবায়নের দায়িত্ব। আমি এই উপায়টি বেছে নিয়েছি কারণ আমি বিভিন্ন দর্শকের প্রয়োগ বাস্তবায়নের জন্য গাছের উপস্থাপনের জন্য স্বাধীনভাবে দর্শন ক্রমের সিদ্ধান্ত নিতে সক্ষম হতে চেয়েছিলাম।

একটি গৌণ সুবিধা হ'ল আমার গাছের নোডগুলির মধ্যে ভিজিটর প্যাটার্নের প্রায় কোনও নিদর্শন নেই - প্রতিটি "স্বীকার করুন" সঠিক কংক্রিটের ধরণের সাথে দর্শনার্থীর জন্য কেবল "ভিজিট" বলে। এটি পরিদর্শন যুক্তি সনাক্ত করা এবং বোঝা সহজ করে তোলে, এটি সবই দর্শকের বাস্তবায়নের অভ্যন্তরে।

স্পষ্টতার জন্য আমি কিছু সি ++ যুক্ত করেছি - ইশ সিডোডকোড। প্রথম নোড:

class INode {
  public:
    virtual void Accept(IVisitor& i_visitor) = 0;
};

class NodeWithChildren : public INode {
  public:
     virtual void Accept(IVisitor& i_visitor) override {
        i_visitor.Visit(*this);
     }
     // Plus interface for getting the children, exercise for the reader ;-)
 };

 class LeafNode : public INode {
   public:
     virtual void Accept(IVisitor& i_visitor) override {
       i_visitor.Visit(*this);
     }
 };

এবং দর্শনার্থী:

class IVisitor {
  public:
     virtual void Visit(NodeWithChildren& i_node) = 0;
     virtual void Visit(LeafNode& i_node) = 0;
};

class ConcreteVisitor : public IVisitor
  public:
     virtual void Visit(NodeWithChildren& i_node) override {
       // Do something useful, then...
       for(Node * p_child : i_node) {
         child->Accept(*this);
       }
     }

     virtual void Visit(LeafNode& i_node) override {
        // Just do something useful, there are no children.
     }

};

1
জন্য +1 allow different Visitor implementations to be able to decide the order of visitation। খুব ভালো বুদ্ধি.
মার্কো ফিসেট

@ মার্কো ফিসেট অ্যালগরিদম (দর্শনার্থী) তখন ডেটা (নোড) কীভাবে কাঠামোগত হয় তা জানতে হবে। এটি দর্শকের প্যাটার্নটি দেয় যে অ্যালগরিদম-ডেটা বিচ্ছেদকে ভেঙে ফেলবে।
বি ভাইসার্স

2
@ বিভিসার্স দর্শনার্থীরা প্রতিটি নোড প্রকারের জন্য একটি ফাংশন বাস্তবায়ন করেন, তাই এটি কোনও নোডের কোনও নির্দিষ্ট সময়ে এটি পরিচালনা করে তা জানে। এতে কিছু ভাঙা যায় না।
মার্কো ফিসেট

3

আপনি পুনরাবৃত্তাকার কাঠামোর চারপাশে ভিজিটর প্যাটার্নটি একইভাবে কাজ করেন আপনি আপনার পুনরাবৃত্ত কাঠামোর সাথে অন্য যে কোনও কাজ করবেন: আপনার কাঠামোর নোডগুলিকে পুনরাবৃত্তি করে দেখে।

public class OperationNode
{
    public int SomeProperty { get; set; }
    public List<OperationNode> Children { get; set; }
}

public static void VisitNode(OperationNode node)
{
    ... Visit this node

    foreach(var node in Children)
    {
         VisitNode(node);
    }
}

public static void VisitAllNodes()
{
    VisitNode(rootNode);
}

এটি পার্সারদের জন্য ব্যর্থ হতে পারে যদি ভাষা গভীরভাবে বাসা বেঁধে দেয় - ভাষার কল স্ট্যাকের স্বাধীনভাবে স্ট্যাক বজায় রাখা প্রয়োজন can
পিট কির্খাম

1
@ পেটকিরখাম: এটি একটি সুন্দর গভীর গাছ হতে হবে।
রবার্ট হার্ভে

@ পেটকিরখাম আপনি কী বলতে চাচ্ছেন যে এটি ব্যর্থ হতে পারে? আপনি কি স্ট্যাকওভারফ্লো এক্সপ্লেশন বা ধারণাটি ভালভাবে মাপবেন না এমন কিছু বোঝাতে চাইছেন? যে মুহুর্তে আমি পারফরম্যান্সের বিষয়ে চিন্তা করি না, আমি কেবল মজা এবং শেখার জন্য এটি করি।
মার্কো ফিসেট

@ মার্কো ফিসেট হ্যাঁ, আপনি যদি বলেন, আপনি স্ট্যাকের ওভারফ্লো ব্যতিক্রম পেয়েছেন, কোনও দর্শকের সাথে একটি বৃহত্তর, গভীর এক্সএমএল ফাইলটি পার্স করার চেষ্টা করুন। আপনি বেশিরভাগ প্রোগ্রামিং ভাষার জন্য এটি থেকে দূরে সরে যাবেন।
পিট কির্খাম
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.