Heim  >  Artikel  >  Web-Frontend  >  Wie die Operatorüberladung verwendet werden sollte

Wie die Operatorüberladung verwendet werden sollte

php中世界最好的语言
php中世界最好的语言Original
2018-06-11 10:22:361219Durchsuche

Dieses Mal zeige ich Ihnen, wie Sie die Operatorüberladung verwenden und welche Vorsichtsmaßnahmen für die Verwendung der Operatorüberladung gelten. Das Folgende ist ein praktischer Fall, schauen wir uns das an.

Ich habe kürzlich Daten verarbeitet und einige Datenstrukturen wie Mat, Vektor, Punkt usw. angepasst. Ich muss die vier arithmetischen Operationen wie Addition, Subtraktion, usw. immer wieder definieren. Multiplikation und Division, Code Es scheint nicht sehr intuitiv zu sein, dass JavaScript keine Operatorüberladung hat, was eine Funktion wie C++ und C# ist. Es ist wirklich ärgerlich, deshalb möchte ich „das Land retten“, indem ich die Operatorüberladung automatisch implementiert Die Implementierungsidee ist eigentlich sehr einfach: Schreiben Sie einfach einen Interpreter. Zum Beispiel:

S = A + B (B - C.fun())/2 + D

übersetzt in

`S = ersetzen (ersetzen(A, '+', ersetzen(ersetzen(B,'',(ersetzen(B,'-',C.fun())))),'/',2),'+',D ) `

In der Ersetzungsfunktion rufen wir die entsprechende Operatorfunktion des Objekts auf. Der Ersetzungsfunktionscode lautet wie folgt:

/**
 * 转换方法
 * @param a
 * @param op
 * @param b
 * @returns {*}
 * @private
 */
export function __replace__(a,op,b){
  if(typeof(a) != 'object' && typeof(b) != 'object'){
    return new Function('a','b','return a' + op + 'b')(a,b)
  }
  if(!Object.getPrototypeOf(a).isPrototypeOf(b)
    && Object.getPrototypeOf(b).isPrototypeOf(a)){
    throw '不同类型的对象不能使用四则运算'
  }
  let target = null
  if (Object.getPrototypeOf(a).isPrototypeOf(b)) {
    target = new Function('return ' + b.__proto__.constructor.name)()
  }
  if (Object.getPrototypeOf(b).isPrototypeOf(a)) {
    target = new Function('return ' + a.__proto__.constructor.name)()
  }
  if (op == '+') {
    if (target.__add__ != undefined) {
      return target.__add__(a, b)
    }else {
      throw target.toString() +'\n未定义__add__方法'
    }
  }else if(op == '-') {
    if (target.__plus__ != undefined) {
      return target.__plus__(a, b)
    }else {
      throw target.toString() + '\n未定义__plus__方法'
    }
  }else if(op == '*') {
    if (target.__multiply__ != undefined) {
      return target.__multiply__(a, b)
    }else {
      throw target.toString() + '\n未定义__multiply__方法'
    }
  } else if (op == '/') {
    if (target.__pide__ != undefined) {
      return target.__pide__(a, b)
    }else {
      throw target.toString() + '\n未定义__pide__方法'
    }
  } else if (op == '%') {
    if (target.__mod__ != undefined) {
      return target.__mod__(a, b)
    }else {
      throw target.toString() + '\n未定义__mod__方法'
    }
  } else if(op == '.*') {
    if (target.__dot_multiply__ != undefined) {
      return target.__dot_multiply__(a, b)
    }else {
      throw target.toString() + '\n未定义__dot_multiply__方法'
    }
  } else if(op == './') {
    if (target.__dot_pide__ != undefined) {
      return target.__dot_pide__(a, b)
    }else {
      throw target.toString() + '\n未定义__dot_pide__方法'
    }
  } else if(op == '**') {
    if (target.__power__ != undefined) {
      return target.__power__(a, b)
    }else {
      throw target.toString() + '\n未定义__power__方法'
    }
  }else {
    throw op + '运算符无法识别'
  }
}

Die Implementierung des Ersetzens ist sehr einfach zu viel erklären. Der wichtige Teil ist, wie der Code implementiert wird. Die Implementierung der vier arithmetischen Operationen beim Studium der Datenstruktur an der Hochschule ist die Grundlage dieser Übersetzung, mit geringfügigen Unterschieden. Beschreiben Sie kurz den Prozess:

1. Teilen Sie den Ausdruck, extrahieren Sie Variablen und Operatoren, um das Metaarray A zu erhalten.
2. Durchlaufen Sie das Metaarray

Wenn es sich bei den Elementen um Addition, Subtraktion und Multiplikation handelt und Division, dann entferne das vorherige Element vom Stapel und konvertiere es in „replace(last, Operator,
Wenn das Element „)“ ist, entferne das Element vom Stapel, spleiße es, bis es auf „(“ trifft, und drücke es in den Stapel. Beachten Sie hier '(' Gibt es einen Funktionsaufruf oder ein Ersetzen vor dem Element? Wenn es sich um einen Funktionsaufruf oder ein Ersetzen handelt, müssen Sie die Daten weiter nach vorne verschieben und die Ersetzungsfunktion schließen.
Wenn dies der Fall ist Ist ein allgemeines Element, prüfen Sie, ob das vorherige Element ersetzt wird, um die Ersetzungsfunktion zu schließen.

3 Erhalten Sie in Schritt 2 den kompilierten Ausdruck. 🎜>Implementieren Sie den Code gemäß dem obigen Prozess:

/**
 * 表达式转换工具方法
 * @param code
 */
export function translate (code) {
  let data = []
  let tmp_code = code.replace(/\s/g,'')
  let tmp = []
  let vari = tmp_code.split(/["]+[^"]*["]+|[']+[^']*[']+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  let ops = tmp_code.match(/["]+[^"]*["]+|[&#39;]+[^&#39;]*[&#39;]+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  for (let i = 0,len = ops.length; i < len; i++) {
    if (vari[i] != &#39;&#39;) {
      tmp.push(vari[i])
    }
    if (ops[i] != &#39;&#39;) {
      tmp.push(ops[i])
    }
  }
  tmp.push(vari[ops.length])
  for (let i = 0; i < tmp.length; i++){
    let item = tmp[i]
    if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/.test(tmp[i])) {
      let top = data.pop()
      let trans = &#39;__replace__(&#39; + top + &#39;,\&#39;&#39; + tmp[i] + &#39;\&#39;,&#39;
      data.push(trans)
    }else{
      if (&#39;)&#39; == tmp[i]) {
        let trans0 = tmp[i]
        let top0 = data.pop()
        while (top0 != &#39;(&#39;) {
          trans0 = top0 + trans0
          top0 = data.pop()
        }
        trans0 = top0 + trans0
        let pre = data[data.length - 1]
        while(/[_\w]+[\.]?[_\w]+/.test(pre)
        && !/^__replace__\(/.test(pre)
        && pre != undefined) {
          pre = data.pop()
          trans0 = pre + trans0
          pre = data[data.length - 1]
        }
        pre = data[data.length - 1]
        while(pre != undefined
        && /^__replace__\(/.test(pre)){
          pre = data.pop()
          trans0 = pre + trans0 + &#39;)&#39;
          pre = data[data.length - 1]
        }
        data.push(trans0)
      }else {
        let pre = data[data.length - 1]
        let trans1 = tmp[i]
        while(pre != undefined
        && /^__replace__\(/.test(pre)
        && !/\*\*|\+|-|\*|\/|\(|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|=|\}|%|\.\/|\.\*/.test(item)
        && !/^__replace__\(/.test(item)) {
          if(tmp[i + 1] == undefined){
            pre = data.pop()
            trans1 = pre + trans1 + &#39;)&#39;
            break;
          }else{
            pre = data.pop()
            trans1 = pre + trans1 + &#39;)&#39;
            pre = data[data.length - 1]
          }
        }
        data.push(trans1)
      }
    }
  }
  let result = &#39;&#39;
  data.forEach((value, key, own) => {
    result += value
  })
  return result
}

Der nächste Schritt besteht darin, den geschriebenen Code von unserem Übersetzer zu übersetzen Dies bedeutet, dass zwei Methoden erforderlich sind: Eine besteht darin, die Methodenattribute im Klassenkonstruktor neu zu definieren, und die andere besteht darin, den Code als Parameter an unsere benutzerdefinierte Methode zu übergeben Konstruktor:

export default class OOkay {
  constructor () {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
    protos.forEach((proto, key, own) => {
      if(proto != 'constructor'){
        Object.defineProperty(this, proto, {
          value:new Function(translate_block(proto, this[proto].toString())).call(this)
        })
      }
    })
  }
}

Wie Sie oben sehen können, verwenden wir Object.defineProperty, um es im Konstruktor neu zu definieren. Der Code lautet wie folgt:

/**
 * 类代码块转换工具
 * @param name
 * @param block
 * @returns {string}
 */
export function translate_block (name , block) {
  let codes = block.split('\n')
  let reg = new RegExp('^' + name + '$')
  console.log(reg.source)
  codes[0] = codes[0].replace(name,'function')
  for(let i = 1; i < codes.length; i++) {
    if (codes[i].indexOf(&#39;//&#39;) != -1) {
      codes[i] = codes[i].substring(0,codes[i].indexOf(&#39;//&#39;))
    }
    if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/g.test(codes[i])){
      if (codes[i].indexOf(&#39;return &#39;) != -1) {
        let ret_index = codes[i].indexOf(&#39;return &#39;) + 7
        codes[i] = codes[i].substring(0,ret_index) + translate(codes[i].substring(ret_index))
      }else {
        let eq_index = codes[i].indexOf(&#39;=&#39;) + 1
        codes[i] = codes[i].substring(0,eq_index) + translate(codes[i].substring(eq_index))
      }
    }
  }
  return &#39;return &#39; + codes.join(&#39;\n&#39;)
}

Für neue Klassen müssen wir nur die OOkay-Klasse erben, um Operatorüberladung in Klassen zu verwenden. Für Code, der von Nicht-OOkay-Klassen erbt, können wir die Injektion wie folgt verwenden:

/**
   * 非继承类的注入方法
   * @param target
   */
  static inject (target) {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(target))
    protos.forEach((proto, key, own) => {
      if (proto != 'constructor') {
        Object.defineProperty(target, proto, {
          value:new Function(translate_block(proto, target[proto].toString())).call(target)
        })
      }
    })
  }

Für Code in Nicht-Klassen benötigen wir einen Container. Hier verwende ich zwei Methoden: Eine Möglichkeit, ookay-Skript zu verwenden, wie folgt: