0

Given the 'ORIGINAL CODE' shown below (from thread Doing a range Lookup)

I'm trying to implement a TableLookUp method that will wrap the BinarySearch routine. I changed the Range class to accept a value property. I created the TableLookUp routine but I know it's wrong. I don't know how to call the BinarySearch method to get this working. Generics is confusing the heck out of me with this.

Tx in advance!

Values called could be like:

var ranges = new Range<int>[]
            {
                new Range<int>(1, 10000, 22),
                new Range<int>(10001, 40000, 33),
                new Range<int>(40001, int.MaxValue, 44)
            };

Replace Range class with the below code:

    public class Range<TValue>
        where TValue : IComparable<TValue>
    {
        public TValue Min { get; set; }
        public TValue Max { get; set; }
        public int Value { get; set; }

        public Range(TValue min, TValue max, int value)
        {
            this.Min = min;
            this.Max = max;
            this.Value = value;
        }
    }

Add wrapper to Binary Search:

public static int LookUpTable<TRange, TValue>(IList<TRange> ranges, TValue value, IRangeComparer<TRange, TValue> comparer)
    {
        int indexToTable = BinarySearch(ranges, value, comparer);
        Range<TRange> lookUp = ranges[indexToTable];
        return lookUp.Value;
    }

Replace the calling code in main to something like this:

Console.WriteLine(LookUpTable(ranges, 7, rangeComparer));
Console.WriteLine(LookUpTable(ranges, 10007, rangeComparer));
Console.WriteLine(LookUpTable(ranges, 40007, rangeComparer));
Console.WriteLine(LookUpTable(ranges, 1, rangeComparer));   

ORIGINAL CODE:

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

namespace TestConsole
{
    class Program
    {

        public interface IRangeComparer<TRange, TValue>
        {
            /// <summary>
            /// Returns 0 if value is in the specified range;
            /// less than 0 if value is above the range;
            /// greater than 0 if value is below the range.
            /// </summary>
            int Compare(TRange range, TValue value);
        }


        /// <summary>
        /// See contract for Array.BinarySearch
        /// </summary>
        public static int BinarySearch<TRange, TValue>(IList<TRange> ranges,
                                                       TValue value,
                                                       IRangeComparer<TRange, TValue> comparer)
        {
            int min = 0;
            int max = ranges.Count - 1;

            while (min <= max)
            {
                int mid = (min + max) / 2;
                int comparison = comparer.Compare(ranges[mid], value);
                if (comparison == 0)
                {
                    return mid;
                }
                if (comparison < 0)
                {
                    min = mid + 1;
                }
                else if (comparison > 0)
                {
                    max = mid - 1;
                }
            }
            return ~min;
        }

        public class Range<TValue>
            where TValue : IComparable<TValue>
        {
            public TValue Min { get; set; }
            public TValue Max { get; set; }

            public Range(TValue min, TValue max)
            {
                this.Min = min;
                this.Max = max;
            }
        }

        public class RangeComparer<TValue> : IRangeComparer<Range<TValue>, TValue>
            where TValue : IComparable<TValue>
        {
            /// <summary>
            /// Returns 0 if value is in the specified range;
            /// less than 0 if value is above the range;
            /// greater than 0 if value is below the range.
            /// </summary>
            public int Compare(Range<TValue> range, TValue value)
            {
                // Check if value is below range (less than min).
                if (range.Min.CompareTo(value) > 0)
                    return 1;

                // Check if value is above range (greater than max)
                if (range.Max.CompareTo(value) < 0)
                    return -1;

                // Value is within range.
                return 0;
            }
        }


        static void Main(string[] args)
        {

            var ranges = new Range<int>[]
            {
                new Range<int>(1, 10000),
                new Range<int>(10001, 40000),
                new Range<int>(40001, int.MaxValue),
            };

            var rangeComparer = new RangeComparer<int>();

            Console.WriteLine(BinarySearch(ranges, 7, rangeComparer));       // gives 0
            Console.WriteLine(BinarySearch(ranges, 10007, rangeComparer));   // gives 1
            Console.WriteLine(BinarySearch(ranges, 40007, rangeComparer));   // gives 2
            Console.WriteLine(BinarySearch(ranges, 1, rangeComparer));       // gives 0
            Console.WriteLine(BinarySearch(ranges, 10000, rangeComparer));   // gives 0
            Console.WriteLine(BinarySearch(ranges, 40000, rangeComparer));   // gives 1
            Console.WriteLine(BinarySearch(ranges, 40001, rangeComparer));   // gives 2

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);
        }
    }
}
Community
  • 1
  • 1
user1161137
  • 1,067
  • 1
  • 11
  • 31

1 Answers1

1
public static int LookUpTable<TRange, TValue>(IList<TRange> ranges, TValue value, IRangeComparer<TRange, TValue> comparer)
    where TRange : Range<TValue> // Specify what you know about TRange and TValue
    where TValue : IComparable<TValue>
{
    int indexToTable = BinarySearch(ranges, value, comparer);
    TRange lookUp = ranges[indexToTable]; // lookUp is TRange, not Range<TRange>
    return lookUp.Value;
}
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Ahh.. ok, all these TRanges were confusing. I had tried just TRange, but then it gives an error with lookUp.Value, stating " 'TRange' does not contain a definition for 'Value' and no extension menthod 'Value'... ". I guess adding the "WHERE TRange: Range" is needed?!? I don't get why? shouldn't it still work, implicitly convertible? – user1161137 Jan 24 '12 at 15:05
  • @user1161137: Implicitly convertible to what? There are many object types that don't have a `Value` property: why should the compiler assume that you're talking about a `Range` object? What if you passed a `List` as the first parameter? Without the `where` filter, there would be nothing preventing that, and yet it wouldn't make sense to access a `.Value` property on an `int`, would it? The `where` filter is the only way for the method to know that you expect `TRange` to represent a `Range` at all. – StriplingWarrior Jan 24 '12 at 15:53
  • StriplingWarrior: Nicely explained. user1161137: Apologies about not following up on the original question, but I’m presumably on a different timezone from you (UTC+1), and only got to read your comment now. – Douglas Jan 24 '12 at 19:36