>  기사  >  웹 프론트엔드  >  연산자 오버로딩을 사용하는 방법

연산자 오버로딩을 사용하는 방법

php中世界最好的语言
php中世界最好的语言원래의
2018-06-11 10:22:361219검색

이번에는 연산자 오버로딩 사용법과 연산자 오버로딩 사용 시 주의사항에 대해 알려드리겠습니다. 다음은 실제 사례입니다.

최근에는 데이터 처리를 해야 하는데 Mat, Vector, Point 등 일부 데이터 구조를 사용자 정의해야 합니다. 덧셈, 뺄셈, 곱셈, 나눗셈 등 4가지 산술 연산을 반복적으로 정의해야 합니다. 코드가 그다지 직관적이지 않고 자바스크립트에는 연산이 없습니다. C++, C#과 같은 기능인 연산자 오버로딩이 정말 귀찮아서 번역 코드에 연산자 오버로딩을 자동으로 구현하여 "국가를 구하고 싶습니다"라는 구현 아이디어입니다. 실제로 매우 간단합니다. 즉, 인터프리터를 작성하고 코드를 컴파일하는 것입니다. 예:

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

`S로 번역됨 = 교체(replace(A, '+', 교체(replace(B, ' ',(replace(B,'-',C.fun())))),'/',2),'+',D)`

replace 함수에서 우리는 다음의 해당 연산자 함수를 호출합니다. object, 교체 함수 코드는 다음과 같습니다.

/**
 * 转换方法
 * @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 + '运算符无法识别'
  }
}

교체 구현은 너무 많은 설명 없이 매우 간단합니다. 중요한 부분은 코드를 컴파일하는 방법입니다. 대학에서 데이터 구조를 공부할 때 네 가지 산술 연산을 구현하는 것이 이 번역의 기초이지만 약간의 차이가 있습니다. 프로세스를 간략하게 설명하세요.

1. 표현식을 분할하고 변수와 연산자를 추출하여 메타배열 A를 얻습니다.
2. 메타배열을 탐색합니다.

요소가 연산자 덧셈, 뺄셈, 곱셈 및 나눗셈인 경우 스택에서 이전 요소를 팝하고
replace(last, 연산자로 변환,
요소가 ')'인 경우 스택에서 요소를 팝하고 '('를 만날 때까지 연결한 다음 스택에 푸시합니다. 여기서 요소가 있는지 여부에 주의해야 합니다. '(' 앞에는 함수 호출 또는 교체가 있습니다. 함수 호출 또는 교체인 경우 계속해서 데이터를 앞으로 팝하고 교체 함수를 닫아야 합니다.

일반 요소인 경우 이전 요소가 있는지 확인하세요. 그렇다면 ')'를 연결하여 교체 기능을 닫아야 합니다.

3. 2단계에서 얻은 스택 시퀀스를 결합하여

위의 프로세스를 거쳐 코드를 구현합니다.

/**
 * 表达式转换工具方法
 * @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
}
표현식 컴파일 방법이 작성됩니다. 다음 단계는 작성된 코드를 변환기로 번역하는 방법입니다. 즉, 컨테이너와 두 가지 메서드가 필요합니다. 하나는 메서드 속성을 재정의하는 것입니다. 다른 하나는 코드를 사용자 정의 메소드에 매개변수로 전달하는 것입니다. 다음으로 클래스 생성자에 재정의 메소드를 도입하겠습니다.

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)
        })
      }
    })
  }
}
위에서 볼 수 있듯이 Object.defineProperty를 사용합니다. 생성자에서 재정의하는 것은 전체 코드 블록을 번역하는 것입니다. 코드는 다음과 같습니다:

/**
 * 类代码块转换工具
 * @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;)
}

새 클래스의 경우 OOkay 클래스를 상속하고 클래스에서 연산자 오버로딩을 사용하면 됩니다. OOOkay 클래스가 아닌 경우 다음과 같이 주입을 사용할 수 있습니다.

/**
   * 非继承类的注入方法
   * @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)
        })
      }
    })
  }

클래스가 아닌 코드의 경우 컨테이너가 필요합니다. 여기서는 두 가지 방법을 사용합니다. 하나는 다음과 같은 ookay 스크립트를 사용하는 것입니다. text/ookayscript'>
let a = a+b // a와 b는 객체입니다 예시

다른 하나는 코드를 __$$__ 메소드에 매개변수로 전달하여 코드를 컴파일하는 것입니다.
static __$__(fn) {
    if(!(fn instanceof Function)){
      throw '参数错误'
    }
    (new Function(translate_block('function',fn.toString()))).call(window)()
  }

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 자세한 내용은 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 읽기:

Vue+Nuxt .js는 서버 측 렌더링을 만듭니다

Angular는 HMR 코드 분석을 사용합니다

위 내용은 연산자 오버로딩을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.