简体   繁体   中英

What is c# alternative of splice in c++

I have a c++ code and trying to write in C#

I couldn't figure out best alternative to splice in c#

Also C++ has a 'find' too to work on map,which I want to implement in C# on dictionary

In your C++ example, you show:

statement_tokens.splice(statement_tokens.begin(), tokens, tokens.begin(), next_sc);

From what I understand ( documentation ), this overload takes an insert position, a list (of the same type), and the first (inclusive) and last (exclusive) indexes of a range to splice into the insert position, and then inserts this range into the original list.

Update: AND it removes the items from the other list. I just added that functionality.

If this is correct, then the following extension method should work:

List Extension Method (check the end of this answer for other overloads of this method)

public static class ListExtensions
{
    public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items, 
        int first, int last)
    {
        if (items == null) return;
        insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
        first = Math.Min(items.Count - 1, Math.Max(0, first));
        last = Math.Min(items.Count, Math.Max(1, last));
        if (first >= last) return;

        list.InsertRange(insertAtIndex, items.GetRange(first, last - first));            
        items.RemoveRange(first, last - first);
    }
}

Update 2 : Now, it looks like you're missing another extension method for std::find_if , which returns the index of a list item in a specified range, based on a method that returns true if the item meets some condition. So let's add the following method to the ListExtensions class above:

public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
{
    if (method == null) return end;
    if (!list.Any(method)) return end;
    start = Math.Min(list.Count - 1, Math.Max(0, start));
    end = Math.Min(list.Count, Math.Max(1, end));
    if (start >= end) return end;
    var range = list.GetRange(start, end - start);
    var index = range.IndexOf(list.First(method));
    if (index < start) return end;
    return index;
}

Notice that one of the arguments to this method is a function that takes an item of type T and returns a bool. This will be a simple method that checks if the string value of our token is a semicolon:

static bool TokenIsSemicolon(EvlToken token)
{
    return (token != null && token.Str == ";");
}

Now, you may notice that I referenced token.Str . This is from the EvlToken class, which was created to mimic the C++ struct :

class EvlToken
{
    public enum TokenType { Name, Number, Single }
    public TokenType Type { get; set; }
    public string Str { get; set; }
    public int LineNo { get; set; }
}

Now we can finish the conversion of the original method, calling our FindIf and Splice extension methods:

static bool MoveTokensToStatement(List<EvlToken> statementTokens, List<EvlToken> tokens)
{
    if (statementTokens == null || statementTokens.Count > 0) return false;
    if (tokens == null || tokens.Count == 0) return false;

    int nextSemiColon = tokens.FindIf(0, tokens.Count, TokenIsSemicolon);

    if (nextSemiColon == tokens.Count)
    {
        Console.WriteLine("Looked for ';' but reached the end of the file.");
        return false;
    }

    ++nextSemiColon;
    statementTokens.Splice(0, tokens, 0, nextSemiColon);
    return true;
}

Additional Overloads

For completeness, here is the extensions class with the other two overloads mentioned in the documentation:

public static class ListExtensions
{
    /// <summary>
    /// Transfers all elements from 'items' into 'this' at the specified index
    /// </summary>
    /// <typeparam name="T">The type of items in the list</typeparam>
    /// <param name="list">'this' instance</param>
    /// <param name="insertAtIndex">The index to insert the items</param>
    /// <param name="items">The list to transfer the items from</param>
    public static void Splice<T>(this List<T> list, int insertAtIndex,
        List<T> items)
    {
        if (items == null) return;
        list.Splice(insertAtIndex, items, 0, items.Count);
    }

    /// <summary>
    /// Transfers the element at 'itemIndex' from 'items' 
    /// into 'this' at the specified index
    /// </summary>
    /// <typeparam name="T">The type of items in the list</typeparam>
    /// <param name="list">'this' instance</param>
    /// <param name="insertAtIndex">The index to insert the item</param>
    /// <param name="items">The list to transfer the item from</param>
    /// <param name="itemIndex">The index of the item to transfer</param>
    public static void Splice<T>(this List<T> list, int insertAtIndex,
        List<T> items, int itemIndex)
    {
        list.Splice(insertAtIndex, items, itemIndex, itemIndex + 1);
    }

    /// <summary>
    /// Transfers the specified range of elements from 'items' 
    /// into 'this' at the specified index
    /// </summary>
    /// <typeparam name="T">The type of items in the list</typeparam>
    /// <param name="list">'this' instance</param>
    /// <param name="insertAtIndex">The index to insert the item</param>
    /// <param name="items">The list to transfer the item from</param>
    /// <param name="first">The index of the first item in the range</param>
    /// <param name="last">The exclusive index of the last item in the range</param>
    public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items, 
        int first, int last)
    {
        if (items == null) return;
        insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
        first = Math.Min(items.Count - 1, Math.Max(0, first));
        last = Math.Min(items.Count, Math.Max(1, last));
        if (first >= last) return;

        list.InsertRange(insertAtIndex, items.GetRange(first, last - first));            
        items.RemoveRange(first, last - first);
    }

    /// <summary>
    /// Searches for the first item in the specified range that "method" returns true for
    /// </summary>
    /// <typeparam name="T">The type of items in the list</typeparam>
    /// <param name="list">'this' instance</param>
    /// <param name="start">The index of the first item in the range</param>
    /// <param name="end">The exclusive index of the last item in the range</param>
    /// <param name="method">A method which takes type 'T' and returns a bool</param>
    /// <returns>The index of the item, if found, otherwise 'end'</returns>
    public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
    {
        if (method == null) return end;
        if (!list.Any(method)) return end;
        start = Math.Min(list.Count - 1, Math.Max(0, start));
        end = Math.Min(list.Count, Math.Max(1, end));
        if (start >= end) return end;
        var range = list.GetRange(start, end - start);
        var index = range.IndexOf(list.First(method));
        if (index < start) return end;
        return index;
    }
}

Example Usage

Here's an example using a list of EvlTokens , and then calling MoveTokensToStatement twice:

private static void Main()
{
    var tokens = new List<EvlToken>
    {
        new EvlToken {LineNo = 3, Str = "int", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 3, Str = "x", Type = EvlToken.TokenType.Name},
        new EvlToken {LineNo = 3, Str = "=", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 3, Str = "1", Type = EvlToken.TokenType.Number},
        new EvlToken {LineNo = 3, Str = "+", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 3, Str = "5", Type = EvlToken.TokenType.Number},
        new EvlToken {LineNo = 3, Str = ";", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = "Console", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = ".", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = "WriteLine", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = "(", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = "Hello World", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = ")", Type = EvlToken.TokenType.Single},
        new EvlToken {LineNo = 4, Str = ";", Type = EvlToken.TokenType.Single}
    };

    var statementTokens = new List<EvlToken>();

    MoveTokensToStatement(statementTokens, tokens);

    Console.WriteLine("Here is the result of calling 'MoveTokensToStatement' the first time:");
    Console.WriteLine(string.Join(" ", statementTokens.Select(t => t.Str)));
    statementTokens.Clear();

    MoveTokensToStatement(statementTokens, tokens);

    Console.WriteLine("\nHere is the result of calling 'MoveTokensToStatement' the second time:");
    Console.WriteLine(string.Join("", statementTokens.Select(t => t.Str)));
    statementTokens.Clear();


    Console.WriteLine("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

Output

在此输入图像描述

@RufusL shows you how to write a method with the same postcondition, but doesn't really discuss any other characteristics of the algorithm. In particular, his algorithm has higher complexity than the C++ splice.

Namely, splice on a doubly linked list such as C++'s std::list is an O(1) operation, because it only requires a constant number of pointer swaps.

.NET does have a doubly linked list class in the base library, which is System.Collections.Generic.LinkedList , but it keeps pointers back to the list from each node ( System.Collections.Generic.LinkedListNode , List property) and each list stores a count. As a result, in addition to the constant number of swaps of the forward and back pointers, O(n) node-to-list pointer updates will be required, and O(n) "distance" calculation is required in order to update the Count field on both lists.

So in order to achieve a true equivalent ( O(1) ) to C++ std::list::splice , one has to abandon the BCL LinkedList class and make a custom doubly linked list, with neither a cached Count field (LINQ Count() that walks the list will still work) nor pointers from list nodes to the list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM