非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

モノイド的論理プログラミング(5)

Prolog のプログラムを

  • C# 形式で書かれた構文を構文解析して
  • C# のクラスに変換する

ということをやりたいので、ChatGPT で調べました。

前回の代数的構造の後、まず以下のように入力しました。

その後以下のように入力しました。

入力: C# に書き換えてください

結果:

using System;
using System.Collections.Generic;
using System.Linq;

namespace PrologEngine
{
    public class Term
    {
        public string Type { get; }
        public string Value { get; }
        public List<Term> Args { get; }

        public Term(string type, string value, List<Term> args = null)
        {
            Type = type;
            Value = value;
            Args = args ?? new List<Term>();
        }

        public override string ToString()
        {
            if (Type == "compound")
                return $"{Value}({string.Join(", ", Args)})";
            return Value;
        }

        public bool Unify(Term other, Dictionary<string, Term> bindings)
        {
            if (Type == "variable")
            {
                if (bindings.ContainsKey(Value))
                {
                    return bindings[Value].Unify(other, bindings);
                }
                bindings[Value] = other;
                return true;
            }

            if (other.Type == "variable")
            {
                return other.Unify(this, bindings);
            }

            if (Type != other.Type || Value != other.Value)
                return false;

            if (Args.Count != other.Args.Count)
                return false;

            for (int i = 0; i < Args.Count; i++)
            {
                if (!Args[i].Unify(other.Args[i], bindings))
                    return false;
            }

            return true;
        }

        public Term Substitute(Dictionary<string, Term> bindings)
        {
            if (Type == "variable" && bindings.ContainsKey(Value))
            {
                return bindings[Value].Substitute(bindings);
            }

            if (Type == "compound")
            {
                var substitutedArgs = Args.Select(arg => arg.Substitute(bindings)).ToList();
                return new Term(Type, Value, substitutedArgs);
            }

            return this;
        }
    }

    public class Literal
    {
        public string Predicate { get; }
        public List<Term> Terms { get; }

        public Literal(string predicate, List<Term> terms)
        {
            Predicate = predicate;
            Terms = terms;
        }

        public override string ToString()
        {
            return $"{Predicate}({string.Join(", ", Terms)})";
        }

        public bool Unify(Literal other, Dictionary<string, Term> bindings)
        {
            if (Predicate != other.Predicate || Terms.Count != other.Terms.Count)
                return false;

            for (int i = 0; i < Terms.Count; i++)
            {
                if (!Terms[i].Unify(other.Terms[i], bindings))
                    return false;
            }

            return true;
        }

        public Literal Substitute(Dictionary<string, Term> bindings)
        {
            var substitutedTerms = Terms.Select(term => term.Substitute(bindings)).ToList();
            return new Literal(Predicate, substitutedTerms);
        }
    }

    public class HornClause
    {
        public Literal Head { get; }
        public List<Literal> Body { get; }

        public HornClause(Literal head, List<Literal> body)
        {
            Head = head;
            Body = body;
        }

        public override string ToString()
        {
            if (Body.Count > 0)
                return $"{Head} :- {string.Join(", ", Body)}.";
            return $"{Head}.";
        }
    }

    public class PrologProgram
    {
        private List<HornClause> Clauses { get; }

        public PrologProgram()
        {
            Clauses = new List<HornClause>();
        }

        public void AddClause(HornClause clause)
        {
            Clauses.Add(clause);
        }

        public IEnumerable<Dictionary<string, Term>> Query(Literal goal)
        {
            return Resolve(new List<Literal> { goal }, new Dictionary<string, Term>());
        }

        private IEnumerable<Dictionary<string, Term>> Resolve(List<Literal> goals, Dictionary<string, Term> bindings)
        {
            if (!goals.Any())
            {
                yield return bindings;
                yield break;
            }

            var currentGoal = goals.First();
            var remainingGoals = goals.Skip(1).ToList();

            foreach (var clause in Clauses)
            {
                var localBindings = new Dictionary<string, Term>(bindings);

                if (clause.Head.Unify(currentGoal, localBindings))
                {
                    var newGoals = clause.Body.Select(l => l.Substitute(localBindings)).ToList();
                    foreach (var result in Resolve(newGoals.Concat(remainingGoals).ToList(), localBindings))
                    {
                        yield return result;
                    }
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Define the Prolog program
            var program = new PrologProgram();

            program.AddClause(new HornClause(
                new Literal("p", new List<Term> {
                    new Term("variable", "X"),
                    new Term("compound", "f", new List<Term> {
                        new Term("constant", "a"),
                        new Term("constant", "b")
                    })
                }),
                new List<Literal> {
                    new Literal("q", new List<Term> {
                        new Term("variable", "X")
                    }),
                    new Literal("r", new List<Term> {
                        new Term("compound", "f", new List<Term> {
                            new Term("constant", "a"),
                            new Term("constant", "b")
                        })
                    })
                }
            ));

            program.AddClause(new HornClause(
                new Literal("q", new List<Term> {
                    new Term("constant", "a")
                }),
                new List<Literal>()
            ));

            program.AddClause(new HornClause(
                new Literal("r", new List<Term> {
                    new Term("compound", "f", new List<Term> {
                        new Term("constant", "a"),
                        new Term("constant", "b")
                    })
                }),
                new List<Literal>()
            ));

            // Query the program
            var goal = new Literal("p", new List<Term> {
                new Term("variable", "Y"),
                new Term("compound", "f", new List<Term> {
                    new Term("constant", "a"),
                    new Term("constant", "b")
                })
            });

            Console.WriteLine("Query: " + goal);
            var results = program.Query(goal);

            Console.WriteLine("Results:");
            foreach (var result in results)
            {
                foreach (var binding in result)
                {
                    Console.WriteLine($"{binding.Key} = {binding.Value}");
                }
            }
        }
    }
}

入力: Python の lark モジュールのグラマーの各要素を C# の(各代数的構造のクラスに変換する)関数定義に書き直すことによって Prolog構文解析C# に書き直してください

結果:

using System;
using System.Collections.Generic;
using System.Linq;

namespace PrologParser
{
    public class Term
    {
        public string Type { get; }
        public string Value { get; }
        public List<Term> Args { get; }

        public Term(string type, string value, List<Term> args = null)
        {
            Type = type;
            Value = value;
            Args = args ?? new List<Term>();
        }

        public override string ToString()
        {
            if (Type == "compound")
                return $"{Value}({string.Join(", ", Args)})";
            return Value;
        }
    }

    public class Literal
    {
        public string Predicate { get; }
        public List<Term> Terms { get; }

        public Literal(string predicate, List<Term> terms)
        {
            Predicate = predicate;
            Terms = terms;
        }

        public override string ToString()
        {
            return $"{Predicate}({string.Join(", ", Terms)})";
        }
    }

    public class HornClause
    {
        public Literal Head { get; }
        public List<Literal> Body { get; }

        public HornClause(Literal head, List<Literal> body)
        {
            Head = head;
            Body = body ?? new List<Literal>();
        }

        public override string ToString()
        {
            if (Body.Count > 0)
                return $"{Head} :- {string.Join(", ", Body)}.";
            return $"{Head}.";
        }
    }

    public static class PrologParser
    {
        public static Term ParseTerm(string input)
        {
            input = input.Trim();
            if (char.IsUpper(input[0]))
            {
                // Variable
                return new Term("variable", input);
            }
            else if (input.Contains('('))
            {
                // Compound term
                var name = input.Substring(0, input.IndexOf('('));
                var argsString = input.Substring(input.IndexOf('(') + 1, input.Length - input.IndexOf('(') - 2);
                var args = ParseArguments(argsString);
                return new Term("compound", name, args);
            }
            else
            {
                // Constant
                return new Term("constant", input);
            }
        }

        public static List<Term> ParseArguments(string input)
        {
            var args = new List<Term>();
            var stack = new Stack<char>();
            var current = "";
            foreach (var ch in input)
            {
                if (ch == ',' && stack.Count == 0)
                {
                    args.Add(ParseTerm(current));
                    current = "";
                }
                else
                {
                    if (ch == '(') stack.Push(ch);
                    if (ch == ')') stack.Pop();
                    current += ch;
                }
            }
            if (!string.IsNullOrWhiteSpace(current))
            {
                args.Add(ParseTerm(current));
            }
            return args;
        }

        public static Literal ParseLiteral(string input)
        {
            var predicate = input.Substring(0, input.IndexOf('('));
            var argsString = input.Substring(input.IndexOf('(') + 1, input.Length - input.IndexOf('(') - 2);
            var terms = ParseArguments(argsString);
            return new Literal(predicate, terms);
        }

        public static HornClause ParseClause(string input)
        {
            var parts = input.Split(new[] { ":-" }, StringSplitOptions.None).Select(p => p.Trim()).ToArray();
            var head = ParseLiteral(parts[0]);

            if (parts.Length > 1)
            {
                var bodyLiterals = parts[1].Split(',').Select(ParseLiteral).ToList();
                return new HornClause(head, bodyLiterals);
            }

            return new HornClause(head, new List<Literal>());
        }

        public static List<HornClause> ParseProgram(string input)
        {
            var clauses = new List<HornClause>();
            var lines = input.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var line in lines)
            {
                clauses.Add(ParseClause(line.Trim()));
            }

            return clauses;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Example Prolog program as a string
            string prologProgram = @"
                p(X, f(a, b)) :- q(X), r(f(a, b)).
                q(a).
                r(f(a, b)).
            ";

            // Parse the program
            var clauses = PrologParser.ParseProgram(prologProgram);

            // Print parsed clauses
            foreach (var clause in clauses)
            {
                Console.WriteLine(clause);
            }
        }
    }
}

プログラムを書いてもらうことはできたので、これを修正していきます。