Thu thập tất cả dữ liệu trong một lần lặp so với sử dụng các hàm cho mã có thể đọc được


8

Nói rằng tôi có một loạt các vận động viên mà tôi cần để tìm ra người chạy cao nhất, người chạy nhanh nhất và người chạy nhẹ nhất. Có vẻ như giải pháp dễ đọc nhất sẽ là:

runners = getRunners();
tallestRunner = getTallestRunner(runners);
fastestRunner = getFastestRunner(runners);
lightestRunner = getLightestRunner(runners);

.. ở mọi nơi, mỗi chức năng lặp lại qua các vận động viên và theo dõi chiều cao lớn nhất, tốc độ lớn nhất và trọng lượng thấp nhất. Lặp lại qua mảng ba lần, tuy nhiên, dường như không phải là một ý tưởng rất tốt. Thay vào đó sẽ tốt hơn để làm:

int greatestHeght, greatestSpeed, leastWeight;
Runner tallestRunner, fastestRunner, lightestRunner;
for(runner in runners){
    if(runner.height > greatestHeight) { greatestHeight = runner.height; tallestRunner = runner; }
    if(runner.speed > ...
}

Mặc dù điều này không quá khó đọc, nhưng nó có thể trở nên lộn xộn khi có nhiều logic hơn cho mỗi phần thông tin được trích xuất trong lần lặp.

Mặt bằng ở đây là gì? Làm thế nào tôi có thể chỉ sử dụng một lần lặp duy nhất trong khi vẫn giữ mã được chia thành các đơn vị logic?


Thật là một sự trùng hợp. Tôi hiện đang suy nghĩ về chính xác cùng một vấn đề (chỉ với tôi đó là sự biến đổi phối hợp).
sleske

Câu trả lời:


6

Taskinoor có ý tưởng đúng, nhưng có một cách tốt hơn để thực hiện nó ... miễn là ngôn ngữ của bạn hỗ trợ chuyển một tham chiếu chức năng xung quanh.

Ví dụ: đây là cách tôi thực hiện theo kiểu C # -ish:

// Define three anonymous functions which take two Runners, compare them, and return one.
Func<Runner, Runner, Runner> tallest = (x,y) => x.height > y.height ? x : y;
Func<Runner, Runner, Runner> fastest = (x,y) => x.speed > y.speed ? x : y;
Func<Runner, Runner, Runner> lightest = (x,y) => x.weight < y.weight ? x : y;

// Default everything to the first runner, to keep things simple 
Runner tallestRunner = fastestRunner = lightestRunner = runners.First();

// Loop
foreach(runner in runners){
    tallestRunner = tallest(tallestRunner, runner);
    fastestRunner = fastest(fastestRunner, runner);
    lightestRunner = lightest(lightestRunner, runner);
}

Điều này là không quan trọng để mở rộng - thay vì xác định ba hàm, bạn có thể xác định một mảng các Func<Runner, Runner, Runner>hàm ẩn danh và chỉ cần chạy tất cả chúng. Bạn thậm chí có thể làm điều đó với các chức năng thông thường như Runner pickTallest(Runner x, Runner y), mặc dù sau đó bạn cần xác định rõ ràng chúng. Tuy nhiên, điều quan trọng là bạn không phải thực sự theo dõi giá trị cho mỗi chỉ số - bạn chỉ cần biết cách so sánh hai Runnersvà chọn một giá trị có giá trị tốt hơn.


2

Đây là một trong những điều mà bạn thường muốn vận hành trên một khối dữ liệu, thay vì nguyên tắc OO của "một mẩu dữ liệu".

Vì vậy, tôi sẽ bao bọc toàn bộ danh sách trong một lớp mà khi tạo, phân tích danh sách và tính toán những gì bạn muốn. Bạn cũng sẽ sử dụng lớp này để chèn vào và xóa khỏi danh sách, để thông tin được bao bọc luôn cập nhật.

class Runners
{
    public Runners( RunnerList inputRunners)
    {
        runners = inputRunners;
        Recalculate();
    }

    private Recalculate()
    {  
       foreach( Runner runner in runners )
       {
           // comparisons here!
       }
    }

    public Insert(Runner newRunner)
    {
        int index = runners.Add(newRunner);
        if( newRunner.height > runners[highestIndex].height)
        {
            highestIndex = index;
        }
        // and so on.
    }

    public Remove(Runner delRunner)
    {
        runners.Remove(delRunner);
        Recalculate();
    }

    // accessors
    Runner GetHighest() { return runners[highestIndex]; }
    Runner GetFastest() { return runners[fastestIndex]; }
    Runner GetLightest() { return runners[lightestIndex]; }

    RunnerList runners; // list of runners we manage
    int highestIndex;   // index of element in list which is highest.
    int fastestIndex;   // index of element in list which is fastest
    int lightestIndex;  // you get the idea right?

}

Đó là nó. bây giờ bạn đã có một khối logic khép kín, trả lời các câu hỏi của bạn cho bạn chỉ với một lần lặp duy nhất khi tạo và khi bạn xóa các đối tượng.


1

Bạn có thể trả lại cả ba giá trị trong một lần. Trong mã giả gần với Scala:

val (fastest, tallest, lightest) = runners
  .foldLeft(...)(((tallestSoFar, fastestSoFar, lightestSoFar),runner) =>
   (tallest(runner, tallestSoFar), 
    fastest(runner, fastestSoFar), 
    lightest(runner, lightestSoFar))

Điều đó sẽ cung cấp cho bạn một loạt các vận động viên bạn đang tìm kiếm. Bạn có thể làm tương tự trong các ngôn ngữ khác. Bạn có thể phải lấy một thư viện như Guava hoặc Underscore để làm điều đó và bạn có thể phải bọc bộ dữ liệu trong một đối tượng.


0

Tạo một lớp RunnerStats để tính toán các số liệu thống kê trong một lần for loop, giống như bạn làm trong đoạn mã thứ hai của mình.

Sau đó, bạn đọc các số liệu thống kê vào các biến thông qua getters. Các getters chỉ trả về các giá trị đã được tính toán, họ không tính toán bất cứ điều gì.

Bằng cách đó bạn sẽ có được giải pháp tốt nhất của cả hai: hiệu quả và mức độ dễ đọc.

runners = getRunners();

RunnerStats rStats = new RunnerStats(runners);

tallest = rStats.getTallets();
fastest = rStats.getFastest();
lightest = rStats.getTallest();

0

Vấn đề bạn đang mô tả rất phổ biến: Bạn có một vòng lặp tạo ra dữ liệu nhất định và bạn muốn tổng hợp nhiều "thống kê" từ dữ liệu tổng thể. Việc triển khai đơn giản là, như bạn đã nói, có nhiều biến cục bộ, mỗi biến được cập nhật trong mỗi lần lặp:

   int greatestHeight, greatestSpeed, leastWeight;
   Runner tallestRunner, fastestRunner, lightestRunner;
   for(...){
      runner = ... // the runner comes from somewhere, not necessarily a list
      if(runner.height > greatestHeight) { greatestHeight = runner.height; tallestRunner = runner; }
      if(runner.speed > ...
   }

Điều này không có sự phân tách mối quan tâm tốt, bởi vì bạn có logic tổng hợp nội tuyến với việc sản xuất dữ liệu. Trích xuất logic tổng hợp thành một phương thức (như được đề xuất trong câu trả lời này ) là một sự cải tiến, nhưng sự cô lập vẫn không tốt: các kết quả trung gian và (nếu cần) các biến trợ giúp như greatestHeightvẫn phải là các biến cục bộ.

Vì vậy, IMHO giải pháp tốt duy nhất là trích xuất cả logic tổng hợp và các bài tập thành một phương thức.

Điều này có thể giải quyết như thế nào? Bằng cách đầu tiên tái cấu trúc các biến cục bộ thành các trường. Sau đó, bạn có thể trích xuất một phương thức updateStats(runner)cập nhật các trường tallestRunner/ fastestRunner/ ... và các trường greatestHeight/ / tương ứng greatestSpeed.

Nhưng điều này không làm cho sự cô lập tồi tệ hơn? Có, lúc đầu, nhưng điều này có thể được sửa bằng cách tái cấu trúc lớp trích xuất : Di chuyển các trường thống kê và updateStatsphương thức sang một lớp mới (ví dụ lồng nhau). Vì vậy, cuối cùng bạn sẽ có giải pháp vượt qua đơn, dễ đọc sau đây:

   RunnerStats stats = new RunnerStats();
   for(...){
       runner = ...
       stats.updateStats(runner);
    }
    ... = stats.tallestRunner;
 }

 static class RunnerStats{
      int greatestHeight, greatestSpeed, leastWeight = Integer.MAX_VALUE;
      Runner tallestRunner, fastestRunner, lightestRunner;

      void updateStats(Runner runner){
          updateTallest(runner);
          update...
      }

      void updateTallest(Runner runner){
           if(runner.height > greatestHeight) { greatestHeight = runner.height; tallestRunner = runner; }
      }
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.