”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 即使是伟大的数学家也会犯错误

即使是伟大的数学家也会犯错误

发布于2024-09-01
浏览:833

We know that math is a science of precision. Can we say the same about GeoGebra, an interactive math learning software? Let's analyze the project source code using PVS-Studio!

Even great mathematicians make mistakes

Introduction

Do you remember how you learned computer science at university? All these matrix and vector multiplications, polynomial equations, interpolation, extrapolation... What if we look at these scary formulas in a real project, rather than in another lab report? What if we dig for issues in such a code base? I suggest running PVS-Studio and dusting off math textbooks. Why textbooks? Let me show you.

Math challenges

One of the main challenges in examining the source code of such programs is to understand what's going on. When reviewing the analyzer report, we've had questions whether the warnings indicate real issues.

Let's take a look at the following fragment:

@Override
public void compute() {
  ....
  if (cumulative != null && cumulative.getBoolean()) {
    ....
  } else {
    ....
    branchAtoMode = fv.wrap().subtract(a).multiplyR(2)
      .divide(bEn.subtract(a).multiply(modeEn.subtract(a)));
    branchModeToB = fv.wrap().subtract(b).multiplyR(2)                
      .divide(bEn.subtract(a).multiply(modeEn.subtract(b)));
      rightBranch = new MyDouble(kernel, 0);
  }
  ....
}

We get the following PVS-Studio warning:

V6072 Two similar code fragments were found. Perhaps, this is a typo and 'b' variable should be used instead of 'a'. AlgoTriangularDF.java 145, AlgoTriangularDF.java 146, AlgoTriangularDF.java 147, AlgoTriangularDF.java 148

Is it really a typo? After a quick research, and once we found the right formula, we can say that everything is written correctly.

The code fragment evaluates the triangular distribution, i.e. the probability density function (PDF) for this distribution. We found the formula:

Even great mathematicians make mistakes

Now let's go through the code.

Here,* fv* is a function variable. wrap returns wrapper, and then the necessary mathematical operations are performed. It's interesting to note that there are both multiply and multiplyR methods. In the second method, R stands for right and swaps operands, as multiplication is not always commutative.

So, the second expression result is written to branchAToMode, and the fourth expression is written to branchModeToB.

We also noticed that in branchModeToB, the signs for the numerator and denominator have been changed. We get the following expression:

Even great mathematicians make mistakes

The expression value didn't change.

So, we refreshed our mathematical knowledge to understand some of the warnings we received. It's not hard to identify if there is a real error in the code, but it's hard to understand what it's supposed to be here instead.

Errors

Lost code segment

Let's start simple and look at the following method:

private void updateSide(int index, int newBottomPointsLength) {
  ....
  GeoSegmentND[] s = new GeoSegmentND[4];
  GeoSegmentND segmentBottom = outputSegmentsBottom.getElement(index);
  s[0] = segmentBottom;
  s[1] = segmentSide1;
  s[2] = segmentSide2;
  s[2] = segmentSide3;
  polygon.setSegments(s);
  polygon.calcArea();
}

We see that someone has forgotten to replace s[2] with s[3]. The last line effect is in all its brilliance. It's the legendary and all-too-common copy-paste error. As a result, the fourth array item is missing and is null!

V6033 An item with the same key '2' has already been changed. AlgoPolyhedronNetPrism.java 376, AlgoPolyhedronNetPrism.java 377

What about values?

Now try to spot the issue in the following code snippet:

static synchronized HashMap getGeogebraMap() {
  ....
  geogebraMap.put("−", "-");
  geogebraMap.put("⊥", "# ");
  geogebraMap.put("∼", "~ ");
  geogebraMap.put("′", "# ");
  geogebraMap.put("≤", Unicode.LESS_EQUAL   "");
  geogebraMap.put("≥", Unicode.GREATER_EQUAL   "");
  geogebraMap.put("∞", Unicode.INFINITY   "");
  ....
  geogebraMap.put("∏", "# ");
  geogebraMap.put("∏", "# ");
  geogebraMap.put("〉", "# ");
  geogebraMap.put("⟩", "# ");
  geogebraMap.put("→", "# ");
  geogebraMap.put("⇒", "# ");
  geogebraMap.put("⟩", "# ");
  geogebraMap.put("→", "# ");
  geogebraMap.put("⇒", "# ");
  geogebraMap.put("→", "# ");
  geogebraMap.put("⋅", "* ");
  geogebraMap.put("∼", "# ");
  geogebraMap.put("∝", "# ");
  geogebraMap.put("∝", "# ");
  geogebraMap.put("∝", "# ");
  geogebraMap.put("⊂", "# ");
  ....
  return geogebraMap;
}

What a magnificent view! It's a joy to read, and this is only a small part because this method starts on line 66 and ends on the 404th. The analyzer issues 50 warnings of the V6033 type. Let's take a quick look at one of these warnings:

V6033 An item with the same key '"∼"' has already been added. MathMLParser.java 229, MathMLParser.java 355

Let's remove the superfluous fragments and look at the expressions referred to the warning:

geogebraMap.put("∼", "~ ");
....
geogebraMap.put("∼", "# ");

It's interesting, though. What is the spacing between the method calls? There are 126 lines. Well, good luck finding such an error by hand!

Most are duplicates in key and value. However, a few cases are similar to the example above, where developers overwrite the value with a different one. Which one should we use?

Circles or ellipses

@Override
protected boolean updateForItSelf() {
  ....
  if (conic.getType() == GeoConicNDConstants.CONIC_SINGLE_POINT) {
    ....
  } else {
    if (visible != Visible.FRUSTUM_INSIDE) {
      ....
      switch (conic.getType()) {
        case GeoConicNDConstants.CONIC_CIRCLE:
          updateEllipse(brush);                     // 



The method for the ellipse is called for both the ellipse and the circle. Indeed, we can assume that this is okay because a circle is also an ellipse. However, the class also has the updateCircle method. What's it supposed to be, then? Let's dive into it a little deeper.

Everything takes place in the DrawConic3D class. Here are the methods for the ellipse and the circle:

protected void updateCircle(PlotterBrush brush) {
  if (visible == Visible.CENTER_OUTSIDE) {
    longitude = brush.calcArcLongitudesNeeded(e1, alpha,
          getView3D().getScale());
    brush.arc(m, ev1, ev2, e1, beta - alpha, 2 * alpha, longitude);
  } else {
    longitude = brush.calcArcLongitudesNeeded(e1, Math.PI,
          getView3D().getScale());
    brush.circle(m, ev1, ev2, e1, longitude);
  }
}
protected void updateEllipse(PlotterBrush brush) {
  if (visible == Visible.CENTER_OUTSIDE) {
    brush.arcEllipse(m, ev1, ev2, e1, e2, beta - alpha, 2 * alpha);
  } else {
    brush.arcEllipse(m, ev1, ev2, e1, e2, 0, 2 * Math.PI);
  }
}

Well... It doesn't give that much confidence. The method bodies are different, but nothing here indicates that we risk displaying unacceptable geometric objects if the method is called incorrectly.

Could there be other clues? A whole single one! The updateCircle method is never used in the project. Meanwhile, updateEllipse is used four times: twice in the first fragment and then twice in DrawConicSection3D, the inheritor class of* DrawConic3D*:

@Override
protected void updateCircle(PlotterBrush brush) {
  updateEllipse(brush);
}

@Override
protected void updateEllipse(PlotterBrush brush) {
  // ....
  } else {
    super.updateEllipse(brush);
  }
}

This updateCircle isn't used, either. So, updateEllipse only has a call in its own override and in the fragment where we first found updateForItSelf. In schematic form, the structure looks like as follows:

Even great mathematicians make mistakes

On the one hand, it seems that the developers wanted to use the all-purpose updateEllipse method to draw a circle. On the other hand, it's a bit strange that DrawConicSection3D has the updateCircle method that calls updateEllipse. However, updateCircle will never be called.

It's hard to guess what the fixed code may look like if the error is actually in the code. For example, if updateCircle needs to call updateEllipse in DrawConicSection3D, but DrawConic3D needs a more optimized algorithm for the circle, the fixed scheme might look like this:

Even great mathematicians make mistakes

So, it seems that developers once wrote updateCircle and then lost it, and we may have found its intended "home". Looks like we have discovered the ruins of the refactoring after which the developers forgot about the "homeless" method. In any case, it's worth rewriting this code to make it clearer so that we don't end up with so many questions.

All these questions have arisen because of the PVS-Studio warning. That's the warning, by the way:

V6067 Two or more case-branches perform the same actions. DrawConic3D.java 212, DrawConic3D.java 215

Order of missing object

private void updateOrdering(GeoElement geo, ObjectMovement movement) {
  ....
  switch (movement) {
    ....
    case FRONT:
      ....
      if (index == firstIndex) {
        if (index != 0) { 
          geo.setOrdering(orderingDepthMidpoint(index));
        }
        else { 
          geo.setOrdering(drawingOrder.get(index - 1).getOrdering() - 1);
        }
      }
    ....
  }
  ....
}

We get the following warning:

V6025 Index 'index - 1' is out of bounds. LayerManager.java 393

This is curious because, in the else block, the index variable is guaranteed to get the value 0. So, we pass -1 as an argument to the get method. What's the result? We catch an IndexOutOfBoundsException.

Triangles

@Override
protected int getGLType(Type type) {
  switch (type) {
  case TRIANGLE_STRIP:
    return GL.GL_TRIANGLE_STRIP;
  case TRIANGLE_FAN:
    return GL.GL_TRIANGLE_STRIP;     // 



The code is new, but the error is already well-known. It's quite obvious that GL.GL_TRIANGLE_STRIP should be GL.GL_TRIANGLE_FAN instead*.* The methods may be similar in some ways, but the results are different. You can read about it under the spoiler.

V6067 Two or more case-branches perform the same actions. RendererImplShadersD.java 243, RendererImplShadersD.java 245

To describe a series of triangles, we need to save the coordinates of the three vertices of each triangle. Thus, given N triangles, we need the saved 3N vertices. If we describe a polyhedral object using a polygon mesh, it's important to know if the triangles are connected. If they are, we can use the Triangle Strip or the Triangle Fan to describe the set of triangles using N 2 vertices.

We note that the Triangle Fan has been removed in Direct3D 10. In OpenGL 4.6, this primitive still exists.

The Triangle Fan uses one center vertex as common, as well as the last vertex and the new vertex. Look at the following example:

Even great mathematicians make mistakes

To describe it, we'd need the entry (A, B, C, D, E, F, G). There are five triangles and seven vertices in the entry.

The Triangle Strip uses the last two vertices and a new one. For instance, we can create the image below using the same sequence of vertices:

Even great mathematicians make mistakes

Therefore, if we use the wrong primitive, we'll get dramatically different results.

Overwritten values

public static void addToJsObject(
JsPropertyMap jsMap, Map map) {
  for (Entry entry : map.entrySet()) {
    Object object = entry.getValue();
      if (object instanceof Integer) {
        jsMap.set(entry.getKey(), unbox((Integer) object));
      } if (object instanceof String[]) {                          //  clean = JsArray.of();
        for (String s: (String[]) object) {
          clean.push(s);
        }
        jsMap.set(entry.getKey(), clean);
    } else {                                                       // 



If we don't look closely, we may not notice the issue right away. However, let's speed up and just check the analyzer message.

V6086 Suspicious code formatting. 'else' keyword is probably missing. ScriptManagerW.java 209

Finally, a more interesting bug is here. The object instanceof String[] check will occur regardless of the result of object instanceof Integer. It may not be a big deal, but there's also the else block that will be executed after a failed check for String[]. As the result, the jsMap value by entry.getKey() will be overwritten if the object was Integer.

There's another similar case, but the potential consequences are a little harder to understand:

public void bkspCharacter(EditorState editorState) {
  int currentOffset = editorState.getCurrentOffsetOrSelection();
  if (currentOffset > 0) {
    MathComponent prev = editorState.getCurrentField()
        .getArgument(currentOffset - 1);
    if (prev instanceof MathArray) {
      MathArray parent = (MathArray) prev;
      extendBrackets(parent, editorState);
    } if (prev instanceof MathFunction) {                          // 



V6086 Suspicious code formatting. 'else' keyword is probably missing. InputController.java 854

Almost correct

Do you often have to write many repetitious checks? Are these checks prone to typos? Let's look at the following method:

public boolean contains(BoundingBox other) {
  return !(isNull() || other.isNull()) && other.minx >= minx
      && other.maxy = miny
      && other.maxy 



V6045 Possible misprint in expression 'other.maxy

We find a typo, we fix it. The answer is simple, there should be other.maxx .

Mixed up numbers

public PointDt[] calcVoronoiCell(TriangleDt triangle, PointDt p) {
  ....
  // find the neighbor triangle
  if (!halfplane.next_12().isHalfplane()) {
    neighbor = halfplane.next_12();
  } else if (!halfplane.next_23().isHalfplane()) {
    neighbor = halfplane.next_23();
  } else if (!halfplane.next_23().isHalfplane()) { // 



Let's see the warning:

V6003 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. DelaunayTriangulation.java 678, DelaunayTriangulation.java 680

We don't even need to figure out what happens to half-planes, because it's clear that the !halfplane.next_31().isHalfplane() check is missing.

Wrong operation

public static ExpressionNode get(
    ExpressionValue left, ExpressionValue right, 
    Operation operation, FunctionVariable fv, 
    Kernel kernel0
) {
  ....
  switch (operation) {
    ....
    case VEC_FUNCTION:
      break;
    case ZETA:
      break;
    case DIRAC:
      return new ExpressionNode(kernel0, left, Operation.DIRAC, fv);
    case HEAVISIDE:
      return new ExpressionNode(kernel0, left, Operation.DIRAC, fv);
    default:
      break;
  }
  ....
}

It seems that, in the second case, Operation.DIRAC should probably be replaced with Operation.HEAVISIDE. Or not? This method is called to obtain the derivative. After researching, we understand what HEAVISIDE *is—the use of *Operation.DIRAC for it is correct. What about the Dirac derivative? This one is a bit tricky to understand. We may trust the developers and suppress the warning. Still, it'd be better if they'd left any explanatory comment as they had done in some cases before.

case FACTORIAL:
  // x! -> psi(x 1) * x!
  return new ExpressionNode(kernel0, left.wrap().plus(1),
      Operation.PSI, null)
          .multiply(new ExpressionNode(kernel0, left,
              Operation.FACTORIAL, null))
          .multiply(left.derivative(fv, kernel0));

case GAMMA:
  // gamma(x) -> gamma(x) psi(x)
  return new ExpressionNode(kernel0, left, Operation.PSI, null)
      .multiply(new ExpressionNode(kernel0, left, Operation.GAMMA,
          null))
      .multiply(left.derivative(fv, kernel0));

And we were motivated to conduct such research by the message of already favorite diagnostic V6067:

V6067 Two or more case-branches perform the same actions. Derivative.java 442, Derivative.java 444

On the one hand, this is an interesting case because we could have a false positive here. On the other, the warning would be useful either way, as it often highlights a genuine error. Regardless of the results, we need to check such code and either fix the error or write explanatory comments. Then the warning can be suppressed.

Vector or point?

private static GeoElement doGetTemplate(Construction cons,
    GeoClass listElement) {
  switch (listElement) {
  case POINT:
    return new GeoPoint(cons);
  case POINT3D:
    return (GeoElement) cons.getKernel().getGeoFactory().newPoint(3, cons);
  case VECTOR:
    return new GeoVector(cons);
  case VECTOR3D:
    return (GeoElement) cons.getKernel().getGeoFactory().newPoint(3, cons);
  }
  return new GeoPoint(cons);
}

The warning isn't hard to guess:

V6067 Two or more case-branches perform the same actions. PointNDFold.java 38, PointNDFold.java 43

Instead of cons.getKernel().getGeoFactory().newPoint(3, cons) in the second case, cons.getKernel().getGeoFactory().newVector(3, cons) may have been intended. If we want to make sure of it, we need to go deeper again. Well, let's dive into it.

So,* getGeoFactory() returns GeoFactory, let's look at the *newPoint *and newVector* methods:

public GeoPointND newPoint(int dimension, Construction cons) {
  return new GeoPoint(cons);
}
public GeoVectorND newVector(int dimension, Construction cons) {
  return new GeoVector(cons);
}

It looks a bit strange. The dimension argument isn't used at all. What's going on here? Inheritance, of course! Let's find the GeoFactory3D inheritor class and see what happens in it.

@Override
public GeoVectorND newVector(int dimension, Construction cons) {
  if (dimension == 3) {
    return new GeoVector3D(cons);
  }
  return new GeoVector(cons);
}
@Override
public GeoPointND newPoint(int dimension, Construction cons) {
  return dimension == 3 ? new GeoPoint3D(cons) : new GeoPoint(cons);
}

Excellent! Now we can admire the creation of four different objects in all their glory. We return to the code fragment with the possible error. For POINT and POINT3D, the objects of the GeoPoint and GeoPoint3D classes are created. GeoVector is created for VECTOR, but poor GeoVector3D seems to have been abandoned.

Sure, it's a bit strange to use a factory method pattern in two cases and call constructors directly in the remaining two. Is this a leftover from the refactoring process, or is it some kind of temporary solution that'll stick around until the end of time? In my opinion, if the responsibility for creating objects has been handed over to another class, it'd be better to fully delegate that responsibility.

Missing quadrillions

@Override
final public void update() {
  ....
  switch (angle.getAngleStyle()) {
    ....
    case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_L:
      // Belgian offset |_
      if (square == null) {
        square = AwtFactory.getPrototype().newGeneralPath();
      } else {
        square.reset();
      }
      length = arcSize * 0.7071067811865;                   // 



Where's the warning? There it is:

V6107 The constant 0.7071067811865 is being utilized. The resulting value could be inaccurate. Consider using Math.sqrt(0.5). DrawAngle.java 303

We found four instances of this code fragment. The more accurate value is 0.7071067811865476. Challenge yourself and try learning it by heart, hehe. The last three digits are omitted. Is it critical? The accuracy is probably enough, but using predefined constants or Math.sqrt(0.5)—as in this case—won't only help in recovering lost digits but also eliminate the risk of typos. Notice how the code readability will enhance. Perhaps when seeing 0.707... someone immediately understands what kind of magic number it is. However, sometimes we can do a little less magic just to enhance code readability.

Conclusion

Our little math journey is over. As we can see, even after tackling many geometric challenges, people may still make simple mistakes in code! Nice if the code is fresh, and developers can still keep their past intentions in mind. In such cases, people may understand what changes are needed if any, but after a long time it may be not so easy to recognize the necessity of fixing these issues.

So, the analyzer proves to be quite efficient right when writing code rather than running it once as I did. Especially now when we have tools like PVS-Studio analyzer at our disposal.

Our other articles on similar topics

  1. Headache from using mathematical software.
  2. Mathematicians: Trust, but Verify.
  3. The Big Calculator Gone Crazy.
  4. Analysis of the Trans-Proteomic Pipeline (TPP) project.
  5. COVID-19 research and uninitialized variable.
  6. Analyzing the code of ROOT, scientific Data Analysis Framework.
  7. NCBI Genome Workbench: Scientific Research under Threat.
版本声明 本文转载于:https://dev.to/anogneva/even-great-mathematicians-make-mistakes-4fi1?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何根据 Python 中的条件替换列表中的值?
    如何根据 Python 中的条件替换列表中的值?
    Python 中根据条件替换列表中的值在 Python 中,您可能会遇到需要操作列表中元素的情况列表,例如根据特定条件替换值。通过利用有效的技术,您可以有效地执行这些修改。一种方法涉及利用列表理解。例如,如果您有一个列表 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 并且想要...
    编程 发布于2024-11-06
  • 如何使用 Docker Scratch 在 Golang 中创建静态二进制文件:CGO_ENABLED=0 和 -ldflags?
    如何使用 Docker Scratch 在 Golang 中创建静态二进制文件:CGO_ENABLED=0 和 -ldflags?
    在 Golang 中创建静态二进制文件的标志当使用 Docker 临时库在 Golang 中构建静态二进制文件时,必须包含 CGO_ENABLED =0 和 -ldflags '-extldflags "-static"' 标志。虽然这两个选项可能看起来多余,但它...
    编程 发布于2024-11-06
  • 我可以将行追加到 CSV 文件而不覆盖它吗?
    我可以将行追加到 CSV 文件而不覆盖它吗?
    在 Python 中向现有 CSV 文件追加新行:一种更有效的方法当您需要使用附加行更新 CSV 文件时,您可能会考虑以下问题:问: 是否可以向现有 CSV 文件添加新行,而无需覆盖和重新创建文件?答: 绝对!以下是将行追加到 CSV 文件的更有效方法:您可以利用Python 中的 with 语句。...
    编程 发布于2024-11-06
  • Nestjs、Firebase、GCloud。如何在 TypeScript 中快速设置 API 后端。
    Nestjs、Firebase、GCloud。如何在 TypeScript 中快速设置 API 后端。
    It's great that you decided to open this article. My name is Fedor, and I've been a full-stack developer on a permanent basis since the end of 2021. J...
    编程 发布于2024-11-06
  • 如何在维护异步操作的同时避免链式函数中的 jQuery Promise?
    如何在维护异步操作的同时避免链式函数中的 jQuery Promise?
    在链式函数中回避 jQuery Promise尽管建议避免 jQuery Promise,但开发人员在不使用 jQuery 的情况下链接异步 jQuery 函数时可能会面临挑战Promise 处理机制,如 .then() 或 .when()。为了解决这个问题,请考虑以下方法:jQuery Promi...
    编程 发布于2024-11-06
  • 为什么“repr”方法在 Python 中至关重要?
    为什么“repr”方法在 Python 中至关重要?
    探索repr方法的意义在Python编程的上下文中,repr 方法在将对象表示为字符串方面起着关键作用。这种简洁而详细的表示有多种用途:repr的目的方法:repr的主要目标方法的目的是返回一个对象的字符串表示形式,该对象既是人类可读的,而且重要的是,是明确的。这种表示应该足以重新创建具有相同状态和...
    编程 发布于2024-11-06
  • 每个开发人员都应该了解可扩展和高效应用程序的顶级 React 设计模式
    每个开发人员都应该了解可扩展和高效应用程序的顶级 React 设计模式
    随着 React 继续主导前端生态系统,掌握其设计模式可以显着提高应用程序的效率和可扩展性。 React 设计模式提供了组织和构建组件、管理状态、处理 props 和提高可重用性的最佳实践。在本博客中,我们将探讨一些关键的 React 设计模式,这些模式可以使您的开发过程从优秀走向卓越。 ...
    编程 发布于2024-11-06
  • 在 React 中构建无限滚动组件
    在 React 中构建无限滚动组件
    介绍 我们在应用程序和网页中看到无限滚动,尤其是希望我们滚动的社交媒体。虽然无意识地滚动不好,但构建自己的无限滚动是很棒的。作为开发人员,我们应该尝试重新创建我们在网上冲浪时看到的组件。它可以挑战您在实现某些组件时了解更多信息并跳出框框进行思考。 此外,如果您希望在应用程序中实现无...
    编程 发布于2024-11-06
  • 在 React 中构建响应式会议图块的动态网格系统
    在 React 中构建响应式会议图块的动态网格系统
    In the era of remote work and virtual meetings, creating a responsive and dynamic grid system for displaying participant video tiles is crucial. Inspi...
    编程 发布于2024-11-06
  • 使用 Spring Boot 和 Spring Cloud 开发微服务
    使用 Spring Boot 和 Spring Cloud 开发微服务
    微服务架构已成为构建可扩展和模块化系统的流行解决方案。通过微服务,您可以将单一应用程序分解为更小的、独立的和专业的服务,这使得系统的维护和发展变得更加容易。在这篇文章中,我们将探讨如何使用 Spring Boot 和 Spring Cloud 创建健壮且高效的微服务。 微服务简介 微服务背后的主要思...
    编程 发布于2024-11-06
  • 克服 PHP DOM XML 解析中的挑战:问题和解决方案
    克服 PHP DOM XML 解析中的挑战:问题和解决方案
    简化 PHP DOM XML 解析:揭开要点当您浏览 PHP DOM 函数的复杂性时,可能会出现某些障碍。为了解决这些挑战,让我们开始了解 DOM 的复杂性,并找出常见问题的解决方案。问题 1:使用 xml:id 驯服 ID当使用 ID 来防止树中出现重复页面时,PHP 的 DOM 遇到了一个难题:...
    编程 发布于2024-11-06
  • 密码重置功能:使用 OTP 重置密码
    密码重置功能:使用 OTP 重置密码
    后端 2. 重置密码 转向下一个 API。 PUT 上 /api/reset-password, req -> otp, email, 新密码, res -> nocontent // controllers/passwordReset.go func ResetP...
    编程 发布于2024-11-06
  • 如何从全局站点包继承 Virtualenv 中的特定包?
    如何从全局站点包继承 Virtualenv 中的特定包?
    从全局站点包继承 Virtualenv 中的特定包为了增强虚拟环境 (virtualenv) 的功能,您可能希望从全局站点继承特定包站点包目录。这种方法允许您有选择地将重要的库合并到您的 virtualenv 中,而无需直接安装它们。继承方法要实现这种继承,请使用以下命令创建一个新的 virtual...
    编程 发布于2024-11-06
  • 如何解决 EF6 中的“找不到 'MySql.Data.MySqlClient\'\”错误?
    如何解决 EF6 中的“找不到 'MySql.Data.MySqlClient\'\”错误?
    MySQL 实体框架的提供程序注册使用 MySQL 和实体框架时,您可能会遇到错误“找不到 Entity Framework 提供程序” 'MySql.Data.MySqlClient' ADO.NET 提供程序。”尽管安装了最新的 MySQL 连接器,您可能仍然会遇到此问题。出现此...
    编程 发布于2024-11-06
  • 如何利用PHP防止邮件传输中的恶意输入?
    如何利用PHP防止邮件传输中的恶意输入?
    保护电子邮件传输的用户输入在 PHP 中,必须在发送电子邮件之前清理用户输入,以防止恶意或有害内容泄露你的系统。考虑下面的简单 PHP 邮件脚本的代码片段:<?php $to = "[email protected]"; $name = $_POST['name']; $m...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3