Làm thế nào để mọi người thoát khỏi các nhánh có điều kiện trong Lập trình hàm?


9

Các trường hợp chuyển đổi chạy dài hoặc các cấu trúc if-if-if được tránh trong OOP bằng cách sử dụng đa hình bất cứ nơi nào nó được áp dụng.

thay vì phân nhánh bằng cách khớp một giá trị, việc phân nhánh được thực hiện ở cấp lớp.

Cách tiếp cận tương tự có thể được áp dụng trong mô hình lập trình chức năng, cụ thể là Clojure?


1
Nó phụ thuộc vào ngôn ngữ. Scala, ví dụ, là một ngôn ngữ chức năng cũng có khả năng hướng đối tượng, vì vậy bạn chỉ cần làm như vậy. Cloure có nhiều hình ảnh. Trong Haskell, bạn có thể có một hàm được xác định trên một kiểu chữ nhất định và kiểu dữ liệu khác nhau có thể cung cấp các triển khai khác nhau của hàm.
Andrea

Tôi không có kinh nghiệm về ngôn ngữ chức năng để thực sự nói, nhưng từ nhỏ tôi biết tôi nghĩ rằng nó có định hướng hơn về lambdas, monad và các hoạt động giống như tập hợp, đặc biệt là sử dụng trình tự và giới thiệu nhanh seq và bất cứ điều gì tương đương với diêm
JustinC

@Andrea: Cảm ơn. Kiểm tra đa phương pháp.
Amogh Talpallikar

Câu trả lời:


13

Họ không tránh chúng, họ ôm lấy họ bằng cú pháp khớp mẫu.

Nhưng lập trình chức năng phần lớn là trực giao với lập trình hướng đối tượng, do đó, phần lớn các ngôn ngữ "chức năng" tuyệt đối cũng hướng đối tượng, bao gồm cả clojure. Trong thực tế, nhiều phương thức của clojure thậm chí còn tốt hơn các phương thức Java đơn giản, bởi vì chúng có thể tự động gửi đi các loại đối số, không chỉ phương thức đầu tiên.

Có một ngôn ngữ chức năng thuần túy không có đa hình động, Haskell. Trong Haskell, bạn có thể định nghĩa nhiều phương thức thông qua các lớp loại, nhưng các loại được giải quyết tại thời gian biên dịch. Để có các loại khác nhau trong thời gian chạy, bạn phải tạo một loại kết hợp và bạn phải viết hàm với khớp mẫu (giống như chuỗi if, nhưng với cú pháp thuận tiện hơn) hoặc yêu cầu trình biên dịch rút ra phương thức bằng cách soạn thảo phương thức phương pháp của các loại cấu thành. Hoặc sử dụng forallphần mở rộng GHC .


Theo hướng đối tượng tôi có nghĩa là ngôn ngữ có một số dạng đa hình động với công văn dựa trên loại thời gian chạy thực tế. Nhiều ngôn ngữ mới chỉ có tính đa hình hóa dựa trên đặc điểm của vùng Viking, trong đó chỉ các giao diện có thể được kế thừa; Tôi coi đó là hướng đối tượng và cho mục đích của câu trả lời này là đủ.


1) Clojure không phải là đối tượng được định hướng bởi bất kỳ ý nghĩa nào của từ mà tôi biết. Nếu bạn tin khác, bạn nên nêu lý lẽ của bạn trong câu trả lời. 2) Clojure không hỗ trợ kế thừa theo nghĩa OO của từ này. Bạn có thể vượn nó như vậy: gist.github.com/david-mcneil/661983 , nhưng tôi sẽ không gọi khả năng hợp nhất các bản đồ điều phối một sơ đồ kế thừa. 3) Đó là một tuyên bố khá táo bạo để nói rằng Haskell là ngôn ngữ duy nhất không có sự kế thừa. Tôi cũng tin rằng nó không chính xác. Nếu bạn tin khác, bạn nên nêu lý lẽ của bạn trong câu trả lời của bạn.
Tim Pote

@Tim, miễn là ngôn ngữ có khả năng xác định các đặc điểm / giao diện cho các đối tượng và có thể có biến tính trạng (bao gồm hoàn toàn động) với công văn dựa trên giá trị thực tế trong thời gian chạy, tôi gọi nó là hướng đối tượng. Đó là tất cả các định hướng đối tượng mà bạn sẽ có trong nhiều ngôn ngữ mới (ví dụ: Go, Rust) và tính kế thừa lớp đang không được khuyến khích trong các ngôn ngữ có ngôn ngữ đó. Lược đồ thừa kế mà bạn liên kết là một kế thừa dựa trên đặc điểm, vì vậy tôi tính nó là hướng đối tượng.
Jan Hudec

@Tim, điểm quảng cáo 3, câu trả lời không nói rằng Haskell chỉ là ngôn ngữ như vậy. Chỉ có điều nó là một ví dụ quan trọng của ngôn ngữ đó. Nơi mà bạn đã thấy từ chỉ duy nhất trong đó?
Jan Hudec

RE: điểm 3: Tôi đọc "có một" là "chỉ một". Thế là đủ công bằng. RE: OO: Java không gửi các giá trị thời gian chạy. Vì vậy, theo định nghĩa của bạn, nó không phải là OO. Haskell có loại công văn, vì vậy theo định nghĩa của bạn, đó là OO. Bên cạnh công văn chỉ là một khía cạnh của OO. Quản lý nhà nước là một yếu tố chính khác. Dù bằng cách nào, tôi không chắc cuộc thảo luận về những gì OO có hoặc không liên quan đến câu hỏi. Vì vậy, bạn chắc chắn có thể loại bỏ bất kỳ cuộc gọi phán xét về nó.
Tim Pote

@TimPote: Java chắc chắn gửi đi giá trị thời gian chạy của invocant (mặc dù không phải là các đối số khác). Haskell có một loại công văn, nhưng trừ khi bạn sử dụng forallphần mở rộng ghc , nó hoàn toàn là thời gian biên dịch.
Jan Hudec

4

Đây là một câu hỏi rất cũ, nhưng tôi cảm thấy như câu trả lời còn thiếu.

Như bạn đã đề cập, trong phân nhánh OO thường được chuyển sang cấp độ lớp. Hãy nghĩ về điều đó có nghĩa là gì:

  1. Phương thức nhà máy xử lý phân nhánh, trả về một lớp dẫn xuất.
  2. Lớp dẫn xuất là một tập hợp các phương thức được sắp xếp hợp lý.
  3. Vì vậy, phương thức nhà máy là một phương thức trả về các phương thức hợp lý.

Đó chính xác là cách bạn sẽ xử lý nó: một hàm bậc cao hơn. Hàm bậc cao hơn của bạn xử lý phân nhánh, trả về các hàm được sắp xếp hợp lý để tiêu thụ.

Trong bối cảnh câu hỏi của bạn, tính đa hình là một chút trừu tượng cho điều đó - với sự an toàn kiểu được thêm vào.


-3

Trong ngôn ngữ lập trình chức năng, chúng ta có thể sử dụng các hàm và thông số chính để thoát khỏi các nhánh có điều kiện. Điều đó có nghĩa là sử dụng các hàm với điều kiện param thay vì "if esle". Xem ví dụ 3. Như computeSphereArea ({radius: 25.55})

Ví dụ 1: OOP // trong OOP (ví dụ sử dụng java (sourceCode từ: http: //developer.51cto.com/art/200907/136506.htm)):

public abstract class Shape {
    //    ...

    public abstract void computeArea();
    public abstract void computeVolume();
    public abstract double getArea();
    public abstract double getVolume();

}
public class Circle extends CircleShape2 {
    //    ...
    double volume = 0.0; //
    public void computeArea() { //
        area = Math.PI * radius * radius;
    }
    public double getArea() {
        return area;
    }
    public void computeVolume() {} //
    public double getVolume() {
        return volume;
    }

}
public class Sphere extends Circle {
    //    ...
    public void computeArea() { //
        super.computeArea(); //
        area = 4 * area;
    }
    public void computeVolume() { //
        super.computeArea(); //
        volume = 4.0 / 3 * radius * area;
    }
}
public class CircleShapeApp {
    public static void main(String[] args) {
        Circle circle = new Circle(12.98);
        Sphere sphere = new Sphere(25.55);

        Shape shape = circle; //
        //
        shape.computeArea();
        shape.computeVolume();
        System.out.println("circle area: " + shape.getArea());
        System.out.println("circle volume: " + shape.getVolume());
        //
        shape = sphere;
        shape.computeArea();
        shape.computeVolume();
        System.out.println("Sphere area: " + shape.getArea());
        System.out.println("Sphere volume: " + shape.getVolume());
    }
}

Ví dụ 2: chức năng như oop. // trong lập trình chức năng (sử dụng javascript chẳng hạn):

function initShape(v) {
    var shape = {};
    v = v || {};
    if (typeOf(v, 'object') === true) {
        shape.volumne = v.volumne || 0.0;
        shape.computeArea = v.computeArea || function() {};
        shape.computeVolume = v.computeVolume || function() {};
        shape.getArea = v.getArea || function() {};
        shape.getVolume = v.getVolume || function() {};
    }

    return shape;
}

function initCircle(v) {
    var circle = {};
    v = v || {};
    if (typeOf(v, 'object') === true) {
        circle.volume = 0.0;
        circle.radius = v.radius || 0.0;
        circle.computeArea = v.computeArea || function() {
            this.area = Math.PI * this.radius * this.radius;
        };
        circle.computeVolume = function() {};
        circle.getArea = v.getArea || function() {
            return this.area
        };
        circle.getVolume = v.getVolume || function() {
            return this.volume
        };
    }
    return initShape(circle);
}

function initSphere(v) {
    var sphere = {}
    v = v || {};
    if (typeOf(v, 'object') === true) {
        var circle = initCircle(v);
        sphere = circle;
        sphere.volume = v.volume;
        sphere.computeArea = function() {
            circle.computeArea();
            this.area = 4 * circle.area;
        }
        sphere.computeVolume = function() {
            circle.computeArea();
            this.volume = 4.0 / 3 * this.radius * circle.area;
        }
    }
    return initShape(sphere);
}
var circle = initCircle(12.98);
circle.computeArea();
circle.computeVolume();
console.log("circle area: " + circle.getArea());
console.log("circle volume: " + circle.getVolume());

var sphere = initShpere(25.55);
sphere.computeArea();
sphere.computeVolume();
console.log("sphere area: " + sphere.getArea());
console.log("sphere volume: " + sphere.getVolume());

// Mặc dù, đây không phải là ví dụ chương trình chức năng thuần túy, nhưng với giao diện chức năng, như initCircle () initSphere (). Bạn có thể tạo thêm các hàm như computeCircleArea () computeSphereArea () làm cho nó có nhiều chức năng hơn. // PS: typeOf () có tại đây: https://github.com/will-v-king/javascript-showMe

Ví dụ3: Ok, chúng ta hãy làm cho nó nhiều chức năng hơn:

/** in functional code shape became meaningless. 
function initShape(v) {
    var shape = {};
    v = v || {};
    if (typeOf(v, 'object') === true) {
        shape = v.object || v.shape || shape;
        shape.volumne = v.volumne || 0.0;
    }
    return shape;
}

function computeShapeArea(v){
}
function computeShapeVolume(v){
}
*/

function initCircle(v) {
    var circle = {};
    v = v || {};
    if (typeOf(v, 'object') === true) {
        circle = v.object || v.circle || circle;
        circle.volume = 0.0;
        circle.radius = v.radius || 0.0;
    }
    return initShape(circle);
}

function computeCircleArea(v){
  var area;
  v = v || {};
  if(typeOf(v) === 'Object'){
    var radius = v.radius || v.object.radius || v.circle.radius;
    if(!typeOf(v,'undefined')){
       area = Math.PI * radius * radius;
    }
  }
  return area;
}
function computeCircleVolume(v){
  return 0.0;
}
/**function initCircle and initSphere are not necessary. why? see the last line.*/
function initSphere(v) {
    var sphere = {}
    v = v || {};
    if (typeOf(v, 'object') === true) {
        var circle = initCircle(v);
        sphere = circle;
        sphere.volume = v.volume;
    }
    return initShape(sphere);
}

function computeSphereArea(v){
  var area;
  v = v || {};
  if(typeOf(v) === 'Object'){
    var radius = v.radius || v.object.radius || v.sphere.radius;
    if(!typeOf(v,'undefined')){
       area = 4 * computeCircleArea({radius:radius});  // **POINT** the same as :circle.computeArea();  this.area = 4 * circle.area;
    }
  }
  return area;
}
function computeSphereVolume(v){
  var volume;
  v = v || {};
  if(typeOf(v,'object') === ture){
    radius = v.radius || typeOf(v.object, 'object') === true ? v.object.radius : typeOf(v.sphere, 'Object') === true ? v.sphere.radius : 0.0;
    var circleArea = computeCircleArea({radius:radius});
    if(typeOf(circleArea,'number')=== true){
      volume = 4.0 / 3 * radius * computeCircleArea({radius:radius}); // **POINT** the same as:    circle.computeArea();  this.volume = 4.0 / 3 * this.radius * circle.area;
    }
  }
  return volume;
}


var circle = initCircle({radius:12.98});
console.log("circle area: " + computeCircleArea(circle) );
console.log("circle volume: " + computeCircleVolume(circle) );

var sphere = initShpere(25.55);
console.log("sphere area: " + computeSphereArea({radius:25.55}) );
console.log("sphere volume: " + computeSphereVolume({radius:25.55}) );
console.log("sphere object is unused.That means initSphere is also not necessary as initShape()");

3
Câu trả lời của bạn sẽ mạnh mẽ hơn nếu bạn giải thích lý do tại sao bạn đưa ra quyết định bạn đã làm với hai ví dụ bạn cung cấp.
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.