2017-01-05 2 views
5

Я пишу код в библиотеке C#, чтобы сделать clustering на (двумерный) набор данных - по существу разбивая данные на группы или кластеры. Чтобы быть полезной, библиотека должна принимать «общие» или «пользовательские» данные, класть ее и возвращать кластерные данные.Передача и возврат пользовательских данных - являются ли интерфейсы правильным подходом?

Для этого мне нужно предположить, что каждый элемент данных в передаваемом наборе данных имеет связанный с ним двумерный вектор (в моем случае Lat, Lng - я работаю с координатами).

Моя первая мысль была использовать общие типы, и проходят в двух списках, один список общих данных (т.е. List<T>), а другой такой же длины, с указанием 2D векторов (т.е. List<Coordinate>, где Coordinate это мой класс для определения пара lat, lng), где списки соответствуют друг другу по индексу. Но это довольно утомительно, потому что это означает, что в алгоритме я должен как-то отслеживать эти индексы.

Моя следующая мысль была использовать inferfaces, где я определить интерфейс

public interface IPoint 
{ 
    double Lat { get; set; } 
    double Lng { get; set; } 
} 

и убедитесь, что данные, которые я прохожу в реализует этот интерфейс (то есть я могу предположить, что каждый элемент данных передается в имеет Lat и a Lng).

Но для меня это тоже не работает. Я использую свою библиотеку C# для остановки кластеров в транзитной сети (в другом проекте). Класс называется Stop, и этот класс также из внешней библиотеки, поэтому я не могу реализовать интерфейс для этого класса.

То, что я тогда был унаследован от Stop, создавая класс под названием ClusterableStop который выглядит следующим образом:

public class ClusterableStop : GTFS.Entities.Stop, IPoint 
{   

    public ClusterableStop(Stop stop) 
    { 
     Id = stop.Id; 
     Code = stop.Code; 
     Name = stop.Name; 
     Description = stop.Description; 
     Latitude = stop.Latitude; 
     Longitude = stop.Longitude; 
     Zone = stop.Zone; 
     Url = stop.Url; 
     LocationType = stop.LocationType; 
     ParentStation = stop.ParentStation; 
     Timezone = stop.Timezone; 
     WheelchairBoarding = stop.WheelchairBoarding; 
    } 
    public double Lat 
    { 
     get 
     { 
      return this.Latitude; 
     } 
    } 

    public double Lng 
    { 
     get 
     { 
      return this.Longitude; 
     } 
    } 
} 

, который, как вы можете видеть, реализующий интерфейс IPoint. Теперь я использую конструктор для ClusterableStop, чтобы сначала преобразовать все Stop s в наборе данных в ClusterableStop s, затем запустить алгоритм и получить результат как ClusterableStop s.

Это не совсем то, что я хочу, потому что я хочу сделать что-то на Stop с основан на том, что кластерные они падают. Я не могу этого сделать, потому что я на самом деле экземпляр новых остановки, а именно ClusterableStop с!

Я все еще могу получить то, что хочу, потому что, например, Я могу восстановить исходные объекты по идентификатору. Но, конечно, есть намного более элегантный способ выполнить все это? Правильно ли это использовать интерфейсы? Казалось, что такая простая идея - переход и возврат пользовательских данных - но оказалось настолько сложным.

ответ

2

Рассматривали ли вы с помощью кортежей, чтобы сделать работу - иногда это полезный способ связывания двух классов, не создавая новый класс. Вы можете создать список кортежей:

List<Tuple<Point, Stop>>

где точка является вещь, которую вы группироваться на.

3

Поскольку все, что вам нужно, чтобы связать (широта, долгота) пару к каждому элементу 2D массива, можно сделать метод, который принимает делегат, который производит соответствующие позиции для каждого элемента данных, как это:

ClusterList Cluster<T>(IList<T> data, Func<int,Coordinate> getCoordinate) { 
    for (int i = 0 ; i != data.Count ; i++) { 
     T item = data[i]; 
     Coordinate coord = getCoord(i); 
     ... 
    } 
} 

Теперь вы можете решить, как Coordinate сопряжен с каждым элементом данных.

Обратите внимание, что ассоциация по положению списка не является единственным вариантом, доступным для вас.Другим вариантом является передать делегат, который принимает элемент и возвращает его координаты:

ClusterList Cluster<T>(IEnumerable<T> data, Func<T,Coordinate> getCoordinate) { 
    foreach (var item in data) { 
     Coordinate coord = getCoord(item); 
     ... 
    } 
} 

Хотя такой подход лучше, чем индекс на основе одного, в тех случаях, когда координаты не доступны на самой, что объект требует, чтобы вызывающий абонент сохранял какой-то ассоциативный контейнер на T, который должен либо хорошо играть с контейнерами на основе хэша, либо быть IComparable<T>. В первом подходе нет ограничений на T.

В вашем случае, второй подход является предпочтительным:

var clustered = Cluster(
    myListOfStops 
, stop => new Coordinate(stop.Latitude, stop.Longitude) 
);