在运行时创建和存储方法链的方法

发布于 2024-09-10 21:15:38 字数 661 浏览 3 评论 0原文

我遇到的问题是,我需要进行大约 40 多次转换才能将松散类型信息转换为存储在 db、xml 文件等中的强类型信息。

我计划用元组标记每种类型,即像这样的转换形式:

host.name.string:host.dotquad.string

它将提供从输入到输出形式的转换。例如,名称存储在字符串类型的主机字段中,输入将转换为字符串类型的点四符号并存储回主机字段。更复杂的转换可能需要几个步骤,每个步骤都通过方法调用来完成,因此是方法链接。

进一步检查上面的示例,元组“host.name.string”的字段主机名称为 www.domain.com。完成 DNS 查找以将域名隐藏为 IP 地址。另一种方法是将 DNS 查找返回的类型更改为 string 类型的 dotquad 内部类型。对于此转换,调用了 4 个单独的方法来从一个元组转换为另一个元组。其他一些转换可能需要更多步骤。

理想情况下,我想要一个关于如何在运行时构建方法链的小例子。开发时方法链接相对简单,但需要一页又一页的代码来涵盖所有可能性,并进行 40 多次转换。

我想到的一种方法是在启动时解析元组,并将链写入程序集,编译它,然后使用反射来加载/访问。它真的很难看,并且会抵消我希望获得的性能提升。

我使用的是 Mono,所以没有 C# 4.0

任何帮助将不胜感激。 鲍勃.

The problem I have is that I need to do about 40+ conversions to convert loosely typed info into strongly typed info stored in db, xml file, etc.

I'm plan to tag each type with a tuple i.e. a transformational form like this:

host.name.string:host.dotquad.string

which will offer a conversion from the input to an output form. For example, the name stored in the host field of type string, the input is converted into a dotquad notation of type string and stored back into host field. More complex conversions may need several steps, with each step being accomplished by a method call, hence method chaining.

Examining further the example above, the tuple 'host.name.string' with the field host of name www.domain.com. A DNS lookup is done to covert domain name to IP address. Another method is applied to change the type returned by the DNS lookup into the internal type of dotquad of type string. For this transformation, there is 4 seperate methods called to convert from one tuple into another. Some other conversions may require more steps.

Ideally I would like an small example of how method chains are constructed at runtime. Development time method chaining is relatively trivial, but would require pages and pages of code to cover all possibilites, with 40+ conversions.

One way I thought of doing is, is parsing the tuples at startup, and writing the chains out to an assembly, compiling it, then using reflection to load/access. Its would be really ugly and negate the performance increases i'm hoping to gain.

I'm using Mono, so no C# 4.0

Any help would be appreciated.
Bob.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

挽手叙旧 2024-09-17 21:15:38

这是一个使用 LINQ 表达式的快速但肮脏的解决方案。您已经表明您想要 C# 2.0,这是 3.5,但它确实可以在 Mono 2.6 上运行。方法链接有点hacky,因为我不完全知道你的版本是如何工作的,所以你可能需要调整表达式代码以适应。

真正的魔力确实发生在 Chainer 类中,它采用字符串集合,代表 MethodChain 子类。采用这样的集合:

{
"string",
"string",
"int"
}

这将生成如下链:

new StringChain(new StringChain(new IntChain()));

Chainer.CreateChain 将返回一个调用 MethodChain.Execute() 的 lambda。由于 Chainer.CreateChain 使用了一些反射,因此速度很慢,但它只需要为每个表达式链运行一次。 lambda 的执行速度几乎与调用实际代码一样快。

希望您能将其融入您的架构中。

public abstract class MethodChain {
private MethodChain[] m_methods;
    private object m_Result;


    public MethodChain(params MethodChain[] methods) {
        m_methods = methods;
    }

    public MethodChain Execute(object expression) {

        if(m_methods != null) {
            foreach(var method in m_methods) {
                expression = method.Execute(expression).GetResult<object>();
            }
        }

        m_Result = ExecuteInternal(expression);
        return this;
    }

    protected abstract object ExecuteInternal(object expression);

    public T GetResult<T>() {
        return (T)m_Result;
    }
}

public class IntChain : MethodChain {

    public IntChain(params MethodChain[] methods)
        : base(methods) {

    }

    protected override object ExecuteInternal(object expression) {
        return int.Parse(expression as string);
    }
}

public class StringChain : MethodChain {

    public StringChain(params MethodChain[] methods):base(methods) {

    }

    protected override object ExecuteInternal(object expression) {
        return (expression as string).Trim();
    }
}


public class Chainer {

    /// <summary>
    /// methods are executed from back to front, so methods[1] will call method[0].Execute before executing itself
    /// </summary>
    /// <param name="methods"></param>
    /// <returns></returns>
    public Func<object, MethodChain> CreateChain(IEnumerable<string> methods) {

        Expression expr = null;
        foreach(var methodName in methods.Reverse()) {

            ConstructorInfo cInfo= null;
            switch(methodName.ToLower()) {
                case "string":
                    cInfo = typeof(StringChain).GetConstructor(new []{typeof(MethodChain[])});
                    break;
                case "int":
                    cInfo = typeof(IntChain).GetConstructor(new[] { typeof(MethodChain[]) });
                    break;
            }
            if(cInfo == null)
                continue;

            if(expr != null)
                expr = Expression.New(cInfo, Expression.NewArrayInit( typeof(MethodChain), Expression.Convert(expr, typeof(MethodChain))));
            else
                expr = Expression.New(cInfo, Expression.Constant(null, typeof(MethodChain[])));
        }

        var objParam = Expression.Parameter(typeof(object));
        var methodExpr = Expression.Call(expr, typeof(MethodChain).GetMethod("Execute"), objParam);
        Func<object, MethodChain> lambda = Expression.Lambda<Func<object, MethodChain>>(methodExpr, objParam).Compile();

        return lambda;
    }
    [TestMethod]
    public void ExprTest() {
        Chainer chainer = new Chainer();
        var lambda = chainer.CreateChain(new[] { "int", "string" });
        var result = lambda(" 34 ").GetResult<int>();
        Assert.AreEqual(34, result);
    }
}

Here is a quick and dirty solution using LINQ Expressions. You have indicated that you want C# 2.0, this is 3.5, but it does run on Mono 2.6. The method chaining is a bit hacky as i didn't exactly know how your version works, so you might need to tweak the expression code to suit.

The real magic really happens in the Chainer class, which takes a collection of strings, which represent the MethodChain subclass. Take a collection like this:

{
"string",
"string",
"int"
}

This will generate a chain like this:

new StringChain(new StringChain(new IntChain()));

Chainer.CreateChain will return a lambda that calls MethodChain.Execute(). Because Chainer.CreateChain uses a bit of reflection, it's slow, but it only needs to run once for each expression chain. The execution of the lambda is nearly as fast as calling actual code.

Hope you can fit this into your architecture.

public abstract class MethodChain {
private MethodChain[] m_methods;
    private object m_Result;


    public MethodChain(params MethodChain[] methods) {
        m_methods = methods;
    }

    public MethodChain Execute(object expression) {

        if(m_methods != null) {
            foreach(var method in m_methods) {
                expression = method.Execute(expression).GetResult<object>();
            }
        }

        m_Result = ExecuteInternal(expression);
        return this;
    }

    protected abstract object ExecuteInternal(object expression);

    public T GetResult<T>() {
        return (T)m_Result;
    }
}

public class IntChain : MethodChain {

    public IntChain(params MethodChain[] methods)
        : base(methods) {

    }

    protected override object ExecuteInternal(object expression) {
        return int.Parse(expression as string);
    }
}

public class StringChain : MethodChain {

    public StringChain(params MethodChain[] methods):base(methods) {

    }

    protected override object ExecuteInternal(object expression) {
        return (expression as string).Trim();
    }
}


public class Chainer {

    /// <summary>
    /// methods are executed from back to front, so methods[1] will call method[0].Execute before executing itself
    /// </summary>
    /// <param name="methods"></param>
    /// <returns></returns>
    public Func<object, MethodChain> CreateChain(IEnumerable<string> methods) {

        Expression expr = null;
        foreach(var methodName in methods.Reverse()) {

            ConstructorInfo cInfo= null;
            switch(methodName.ToLower()) {
                case "string":
                    cInfo = typeof(StringChain).GetConstructor(new []{typeof(MethodChain[])});
                    break;
                case "int":
                    cInfo = typeof(IntChain).GetConstructor(new[] { typeof(MethodChain[]) });
                    break;
            }
            if(cInfo == null)
                continue;

            if(expr != null)
                expr = Expression.New(cInfo, Expression.NewArrayInit( typeof(MethodChain), Expression.Convert(expr, typeof(MethodChain))));
            else
                expr = Expression.New(cInfo, Expression.Constant(null, typeof(MethodChain[])));
        }

        var objParam = Expression.Parameter(typeof(object));
        var methodExpr = Expression.Call(expr, typeof(MethodChain).GetMethod("Execute"), objParam);
        Func<object, MethodChain> lambda = Expression.Lambda<Func<object, MethodChain>>(methodExpr, objParam).Compile();

        return lambda;
    }
    [TestMethod]
    public void ExprTest() {
        Chainer chainer = new Chainer();
        var lambda = chainer.CreateChain(new[] { "int", "string" });
        var result = lambda(" 34 ").GetResult<int>();
        Assert.AreEqual(34, result);
    }
}
爱的那么颓废 2024-09-17 21:15:38

命令模式适合这里。您可以做的是将命令排队,因为您需要对不同的数据类型执行不同的操作。当您稍后准备好时,这些消息都可以被处理并调用适当的方法。

此模式可以在.NET 2.0 中实现。

The command pattern would fit here. What you could do is queue up commands as you need different operations performed on the different data types. Those messages could then all be processed and call the appropriate methods when you're ready later on.

This pattern can be implemented in .NET 2.0.

最初的梦 2024-09-17 21:15:38

您真的需要在执行时执行此操作吗?不能使用代码生成来创建操作组合吗?

让我详细说明一下:

假设您有一个名为 Conversions 的类,其中包含您提到的所有 40 多个转换,如下所示:

//just pseudo code.. 
class conversions{

string host_name(string input){}
string host_dotquad(string input){}
int type_convert(string input){}
float type_convert(string input){}
float increment_float(float input){}

} 

编写一个简单的控制台应用程序或类似的东西,它使用反射来为如下方法生成代码:

execute_host_name(string input, Queue<string> conversionQueue)
{
    string ouput = conversions.host_name(input);

    if(conversionQueue.Count == 0)
        return output;

    switch(conversionQueue.dequeue())
    {
        // generate case statements only for methods that take in 
        // a string as parameter because the host_name method returns a string. 
        case "host.dotquad": return execute_host_dotquad(output,conversionQueue);
        case "type.convert": return execute_type_convert(output, conversionQueue);
        default: // exception...
    }
}

将所有这些包装在一个不错的小执行中像这样的方法:

object execute(string input, string [] conversions)
{
    Queue<string> conversionQueue = //create the queue..

    case(conversionQueue.dequeue())
    {
        case "host.name": return execute_host_name(output,conversionQueue);
        case "host.dotquad": return execute_host_dotquad(output,conversionQueue);
        case "type.convert": return execute_type_convert(output, conversionQueue);
        default: // exception...
    }
}

仅当您的方法签名更改或决定添加新的转换时,才需要执行此代码生成应用程序。

主要优点:

  • 无运行时开销
  • 易于添加/删除/更改转换(代码生成器将处理代码更改:))

您觉得怎么样?

Do you really need to do this at execution time? Can't you create the combination of operations using code generation?

Let me elaborate:

Assuming you have a class called Conversions which contains all the 40+ convertions you mentioned like this:

//just pseudo code.. 
class conversions{

string host_name(string input){}
string host_dotquad(string input){}
int type_convert(string input){}
float type_convert(string input){}
float increment_float(float input){}

} 

Write a simple console app or something similar which uses reflection to generate code for methods like this:

execute_host_name(string input, Queue<string> conversionQueue)
{
    string ouput = conversions.host_name(input);

    if(conversionQueue.Count == 0)
        return output;

    switch(conversionQueue.dequeue())
    {
        // generate case statements only for methods that take in 
        // a string as parameter because the host_name method returns a string. 
        case "host.dotquad": return execute_host_dotquad(output,conversionQueue);
        case "type.convert": return execute_type_convert(output, conversionQueue);
        default: // exception...
    }
}

Wrap all this in a Nice little execute method like this:

object execute(string input, string [] conversions)
{
    Queue<string> conversionQueue = //create the queue..

    case(conversionQueue.dequeue())
    {
        case "host.name": return execute_host_name(output,conversionQueue);
        case "host.dotquad": return execute_host_dotquad(output,conversionQueue);
        case "type.convert": return execute_type_convert(output, conversionQueue);
        default: // exception...
    }
}

This code generation application need to be executed only when your method signatures changes or when you decide to add new transformations.

Main advantages:

  • No runtime overhead
  • Easy to add/delete/change the conversions (code generator will take care of the code changes :) )

What do you think?

笨死的猪 2024-09-17 21:15:38

我为冗长的代码转储以及它是用 Java 而不是 C# 的事实表示歉意,但我发现你的问题非常有趣,而且我没有太多 C# 经验。希望您能够毫无困难地适应此解决方案。

解决问题的一种方法是为每次转换创建成本 - 通常这与转换的准确性有关 - 然后执行搜索以找到从一种类型到另一种类型的最佳可能转换序列。

需要成本函数的原因是在多个转化路径中进行选择。例如,从整数转换为字符串是无损的,但不能保证每个字符串都可以用整数表示。因此,如果您有两个转换链

  • string ->整数->浮动->十进制
  • 字符串 ->浮动->小数

您可能需要选择第二个,因为它将减少转换失败的可能性。

下面的 Java 代码实现了这样的方案,并执行最佳优先搜索来找到最佳转换序列。我希望你觉得它有用。运行代码会产生以下输出:

> No conversion possible from string to integer 
> The optimal conversion sequence from string to host.dotquad.string is:
>               string to     host.name.string, cost = -1.609438
>     host.name.string to             host.dns, cost = -1.609438 *PERFECT*
>             host.dns to         host.dotquad, cost = -1.832581
>         host.dotquad to  host.dotquad.string, cost = -1.832581 *PERFECT*

这是 Java 代码。

/**
 * Use best-first search to find an optimal sequence of operations for
 * performing a type conversion with maximum fidelity.
 */
import java.util.*;

public class TypeConversion {

    /**
     * Define a type-conversion interface.  It converts between to
     * user-defined types and provides a measure of fidelity (accuracy)
     * of the conversion.
     */
    interface ITypeConverter<T, F> {
        public T convert(F from);
        public double fidelity();

        // Could use reflection instead of handling this explicitly
        public String getSourceType();
        public String getTargetType();
    }

    /**
     * Create a set of user-defined types.
     */
    class HostName {
        public String hostName;
        public HostName(String hostName) { 
            this.hostName = hostName; 
        }
    }

    class DnsLookup {
        public String ipAddress;    
        public DnsLookup(HostName hostName) {
            this.ipAddress = doDNSLookup(hostName);
        }
        private String doDNSLookup(HostName hostName) {
            return "127.0.0.1";
        }
    }

    class DottedQuad {
        public int[] quad = new int[4];
        public DottedQuad(DnsLookup lookup) {
            String[] split = lookup.ipAddress.split(".");
            for ( int i = 0; i < 4; i++ )
                quad[i] = Integer.parseInt( split[i] );
        }
    }

    /**
     * Define a set of conversion operations between the types. We only
     * implement a minimal number for brevity, but this could be expanded.
     * 
     * We start by creating some broad classes to differentiate among
     * perfect, good and bad conversions.
     */
    abstract class PerfectTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 1.0; }
    }

    abstract class GoodTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 0.8; }
    }

    abstract class BadTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 0.2; }
    }

    /**
     * Concrete classes that do the actual conversions.
     */
    class StringToHostName extends BadTypeConversion<HostName, String> {
        public HostName convert(String from) { return new HostName(from); }
        public String getSourceType() { return "string"; }
        public String getTargetType() { return "host.name.string"; }
    }

    class HostNameToDnsLookup extends PerfectTypeConversion<DnsLookup, HostName> {
        public DnsLookup convert(HostName from) { return new DnsLookup(from); }
        public String getSourceType() { return "host.name.string"; }
        public String getTargetType() { return "host.dns"; }
    }

    class DnsLookupToDottedQuad extends GoodTypeConversion<DottedQuad, DnsLookup> {
        public DottedQuad convert(DnsLookup from) { return new DottedQuad(from); }
        public String getSourceType() { return "host.dns"; }
        public String getTargetType() { return "host.dotquad"; }
    }

    class DottedQuadToString extends PerfectTypeConversion<String, DottedQuad> {
        public String convert(DottedQuad f) { 
            return f.quad[0] + "." + f.quad[1] + "." + f.quad[2] + "." + f.quad[3]; 
        }
        public String getSourceType() { return "host.dotquad"; }
        public String getTargetType() { return "host.dotquad.string"; }
    }

    /**
     * To find the best conversion sequence, we need to instantiate
     * a list of converters.
     */
    ITypeConverter<?,?> converters[] = 
    { 
       new StringToHostName(),
       new HostNameToDnsLookup(),
       new DnsLookupToDottedQuad(),
       new DottedQuadToString()
    };

    Map<String, List<ITypeConverter<?,?>>> fromMap = 
        new HashMap<String, List<ITypeConverter<?,?>>>();

    public void buildConversionMap() 
    {
        for ( ITypeConverter<?,?> converter : converters ) 
        {
            String type = converter.getSourceType();
            if ( !fromMap.containsKey( type )) {
                fromMap.put( type, new ArrayList<ITypeConverter<?,?>>());
            }

            fromMap.get(type).add(converter);
        }
    }

    public class Tuple implements Comparable<Tuple> 
    {
        public String type;
        public double cost;
        public Tuple parent;

        public Tuple(String type, double cost, Tuple parent) {
            this.type = type;
            this.cost = cost;
            this.parent = parent;
        }

        public int compareTo(Tuple o) {
            return Double.compare( cost, o.cost );
        }
    }

    public Tuple findOptimalConversionSequence(String from, String target)
    {
        PriorityQueue<Tuple> queue = new PriorityQueue<Tuple>();

        // Add a dummy start node to the queue
        queue.add( new Tuple( from, 0.0, null ));

        // Perform the search
        while ( !queue.isEmpty() )
        {
            // Pop the most promising candidate from the list
            Tuple tuple = queue.remove();

            // If the type matches the target type, return
            if ( tuple.type == target )
                return tuple;

            // If we have reached a dead-end, backtrack
            if ( !fromMap.containsKey( tuple.type ))
                continue;

            // Otherwise get all of the possible conversions to
            // perform next and add their costs
            for ( ITypeConverter<?,?> converter : fromMap.get( tuple.type ))
            {
                String type = converter.getTargetType();
                double cost = tuple.cost + Math.log( converter.fidelity() );

                queue.add( new Tuple( type, cost, tuple ));
            }
        }

        // No solution
        return null;
    }

    public static void convert(String from, String target)
    {
        TypeConversion tc = new TypeConversion();

        // Build a conversion lookup table
        tc.buildConversionMap();

        // Find the tail of the optimal conversion chain.
        Tuple tail = tc.findOptimalConversionSequence( from, target );

        if ( tail == null ) {
            System.out.println( "No conversion possible from " + from + " to " + target );
            return;
        }

        // Reconstruct the conversion path (skip dummy node)
        List<Tuple> solution = new ArrayList<Tuple>();
        for ( ; tail.parent != null ; tail = tail.parent ) 
            solution.add( tail );

        Collections.reverse( solution );

        StringBuilder sb = new StringBuilder();
        Formatter formatter = new Formatter(sb);

        sb.append( "The optimal conversion sequence from " + from + " to " + target + " is:\n" );
        for ( Tuple tuple : solution ) {
            formatter.format( "%20s to %20s, cost = %f", tuple.parent.type, tuple.type, tuple.cost );       

            if ( tuple.cost == tuple.parent.cost )
                sb.append( " *PERFECT*");

            sb.append( "\n" );      
        }

        System.out.println( sb.toString() );
    }

    public static void main(String[] args) 
    {
        // Run two tests
        convert( "string", "integer" );
        convert( "string", "host.dotquad.string" );     
    }
}

I apologize for the long code dump and the fact that it is in Java, rather than C#, but I found your problem quite interesting and I do not have much C# experience. Hopefully you will be able to adapt this solution without difficulty.

One approach to solving your problem is to create a cost for each conversion -- usually this is related to the accuracy of the conversion -- and then perform a search to find the best possible conversion sequence to get from one type to another.

The reason for needing a cost function is to choose among multiple conversion paths. For example, converting from an integer to a string is lossless, but there is no guarantee that every string can be represented by an integer. So, if you had two conversion chains

  • string -> integer -> float -> decimal
  • string -> float -> decimal

You would want to select the second one because it will reduce the chance of a conversion failure.

The Java code below implements such a scheme and performs a best-first search to find an optimal conversion sequence. I hope you find it useful. Running the code produces the following output:

> No conversion possible from string to integer 
> The optimal conversion sequence from string to host.dotquad.string is:
>               string to     host.name.string, cost = -1.609438
>     host.name.string to             host.dns, cost = -1.609438 *PERFECT*
>             host.dns to         host.dotquad, cost = -1.832581
>         host.dotquad to  host.dotquad.string, cost = -1.832581 *PERFECT*

Here is the Java code.

/**
 * Use best-first search to find an optimal sequence of operations for
 * performing a type conversion with maximum fidelity.
 */
import java.util.*;

public class TypeConversion {

    /**
     * Define a type-conversion interface.  It converts between to
     * user-defined types and provides a measure of fidelity (accuracy)
     * of the conversion.
     */
    interface ITypeConverter<T, F> {
        public T convert(F from);
        public double fidelity();

        // Could use reflection instead of handling this explicitly
        public String getSourceType();
        public String getTargetType();
    }

    /**
     * Create a set of user-defined types.
     */
    class HostName {
        public String hostName;
        public HostName(String hostName) { 
            this.hostName = hostName; 
        }
    }

    class DnsLookup {
        public String ipAddress;    
        public DnsLookup(HostName hostName) {
            this.ipAddress = doDNSLookup(hostName);
        }
        private String doDNSLookup(HostName hostName) {
            return "127.0.0.1";
        }
    }

    class DottedQuad {
        public int[] quad = new int[4];
        public DottedQuad(DnsLookup lookup) {
            String[] split = lookup.ipAddress.split(".");
            for ( int i = 0; i < 4; i++ )
                quad[i] = Integer.parseInt( split[i] );
        }
    }

    /**
     * Define a set of conversion operations between the types. We only
     * implement a minimal number for brevity, but this could be expanded.
     * 
     * We start by creating some broad classes to differentiate among
     * perfect, good and bad conversions.
     */
    abstract class PerfectTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 1.0; }
    }

    abstract class GoodTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 0.8; }
    }

    abstract class BadTypeConversion<T, F> implements ITypeConverter<T, F> {
        public abstract T convert(F from);
        public double fidelity() { return 0.2; }
    }

    /**
     * Concrete classes that do the actual conversions.
     */
    class StringToHostName extends BadTypeConversion<HostName, String> {
        public HostName convert(String from) { return new HostName(from); }
        public String getSourceType() { return "string"; }
        public String getTargetType() { return "host.name.string"; }
    }

    class HostNameToDnsLookup extends PerfectTypeConversion<DnsLookup, HostName> {
        public DnsLookup convert(HostName from) { return new DnsLookup(from); }
        public String getSourceType() { return "host.name.string"; }
        public String getTargetType() { return "host.dns"; }
    }

    class DnsLookupToDottedQuad extends GoodTypeConversion<DottedQuad, DnsLookup> {
        public DottedQuad convert(DnsLookup from) { return new DottedQuad(from); }
        public String getSourceType() { return "host.dns"; }
        public String getTargetType() { return "host.dotquad"; }
    }

    class DottedQuadToString extends PerfectTypeConversion<String, DottedQuad> {
        public String convert(DottedQuad f) { 
            return f.quad[0] + "." + f.quad[1] + "." + f.quad[2] + "." + f.quad[3]; 
        }
        public String getSourceType() { return "host.dotquad"; }
        public String getTargetType() { return "host.dotquad.string"; }
    }

    /**
     * To find the best conversion sequence, we need to instantiate
     * a list of converters.
     */
    ITypeConverter<?,?> converters[] = 
    { 
       new StringToHostName(),
       new HostNameToDnsLookup(),
       new DnsLookupToDottedQuad(),
       new DottedQuadToString()
    };

    Map<String, List<ITypeConverter<?,?>>> fromMap = 
        new HashMap<String, List<ITypeConverter<?,?>>>();

    public void buildConversionMap() 
    {
        for ( ITypeConverter<?,?> converter : converters ) 
        {
            String type = converter.getSourceType();
            if ( !fromMap.containsKey( type )) {
                fromMap.put( type, new ArrayList<ITypeConverter<?,?>>());
            }

            fromMap.get(type).add(converter);
        }
    }

    public class Tuple implements Comparable<Tuple> 
    {
        public String type;
        public double cost;
        public Tuple parent;

        public Tuple(String type, double cost, Tuple parent) {
            this.type = type;
            this.cost = cost;
            this.parent = parent;
        }

        public int compareTo(Tuple o) {
            return Double.compare( cost, o.cost );
        }
    }

    public Tuple findOptimalConversionSequence(String from, String target)
    {
        PriorityQueue<Tuple> queue = new PriorityQueue<Tuple>();

        // Add a dummy start node to the queue
        queue.add( new Tuple( from, 0.0, null ));

        // Perform the search
        while ( !queue.isEmpty() )
        {
            // Pop the most promising candidate from the list
            Tuple tuple = queue.remove();

            // If the type matches the target type, return
            if ( tuple.type == target )
                return tuple;

            // If we have reached a dead-end, backtrack
            if ( !fromMap.containsKey( tuple.type ))
                continue;

            // Otherwise get all of the possible conversions to
            // perform next and add their costs
            for ( ITypeConverter<?,?> converter : fromMap.get( tuple.type ))
            {
                String type = converter.getTargetType();
                double cost = tuple.cost + Math.log( converter.fidelity() );

                queue.add( new Tuple( type, cost, tuple ));
            }
        }

        // No solution
        return null;
    }

    public static void convert(String from, String target)
    {
        TypeConversion tc = new TypeConversion();

        // Build a conversion lookup table
        tc.buildConversionMap();

        // Find the tail of the optimal conversion chain.
        Tuple tail = tc.findOptimalConversionSequence( from, target );

        if ( tail == null ) {
            System.out.println( "No conversion possible from " + from + " to " + target );
            return;
        }

        // Reconstruct the conversion path (skip dummy node)
        List<Tuple> solution = new ArrayList<Tuple>();
        for ( ; tail.parent != null ; tail = tail.parent ) 
            solution.add( tail );

        Collections.reverse( solution );

        StringBuilder sb = new StringBuilder();
        Formatter formatter = new Formatter(sb);

        sb.append( "The optimal conversion sequence from " + from + " to " + target + " is:\n" );
        for ( Tuple tuple : solution ) {
            formatter.format( "%20s to %20s, cost = %f", tuple.parent.type, tuple.type, tuple.cost );       

            if ( tuple.cost == tuple.parent.cost )
                sb.append( " *PERFECT*");

            sb.append( "\n" );      
        }

        System.out.println( sb.toString() );
    }

    public static void main(String[] args) 
    {
        // Run two tests
        convert( "string", "integer" );
        convert( "string", "host.dotquad.string" );     
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文