编程笔记

lifelong learning & practice makes perfect

理解谷歌在量子纠错方面的突破

对谷歌最新量子纠错论文以及用于容错量子计算的实时解码的未来进行控制者视角的解读。

想象一下,试图同时平衡数千个旋转的陀螺——每个陀螺代表一个量子比特,量子计算机的基本构建块。现在想象一下,这些陀螺非常敏感,即使是微风、轻微的振动或快速一瞥看看它们是否还在旋转,都可能使它们摇晃或掉落。这就是量子计算的挑战:量子比特非常脆弱,即使是控制或测量它们的过程也会引入错误。

这就是量子纠错(QEC)的用武之地。通过将多个脆弱的物理量子比特组合成一个更强大的逻辑量子比特,QEC 使我们能够比错误累积得更快地纠正错误。目标是在低于临界阈值的情况下运行——即增加更多量子比特会减少而不是增加错误的点。这正是 Google Quantum AI 近期突破所实现的 [1]

谷歌的突破性成就

要理解谷歌成果的意义,首先让我们了解一下纠错的成功是什么样的。在经典计算机中,抗错误的内存是通过复制比特来检测和纠正错误来实现的。一种称为多数投票的方法经常被使用,其中比较一个比特的多个副本,并将多数值作为正确的比特。在量子系统中,物理量子比特被组合起来创建逻辑量子比特,其中通过监测量子比特之间的相关性而不是直接观察量子比特本身来纠正错误。它涉及像多数投票这样的冗余,但不依赖于观察而是纠缠。这种间接方法至关重要,因为直接测量量子比特的状态会破坏其量子特性。有效的量子纠错可以保持逻辑量子比特的完整性,即使某些物理量子比特出现错误,这对于可扩展的量子计算至关重要。

然而,这只有在物理错误率低于临界阈值时才有效。事实上,直觉认为,增加构成逻辑量子比特的物理量子比特的数量应该可以实现更好的纠错。事实上,如果每个物理量子比特都非常容易出错,那么增加量子比特会使错误累积得比我们检测和纠正它们的速度更快。换句话说,只有在任何纠错之前,每个量子比特都可以在低于错误阈值的情况下运行,量子纠错才有效。拥有更多的物理量子比特可以增加 QEC 代码距离,这是衡量量子代码检测和纠正错误的能力的指标。

通过展示当代码距离从 5 增加到 7 时,逻辑错误减少了 2.14 倍,谷歌现在已经证明了使用表面代码(一种特定类型的量子纠错代码)的低于阈值运行。这种误差的减少(随着代码距离的增加呈指数级增长)是证明其 QEC 策略有效的确凿证据。有了这一点,谷歌可以证明他们的逻辑量子比特的寿命是他们最好的物理量子比特的两倍以上,如图 1 所示,这表明逻辑量子比特不仅存活下来,而且性能优于物理量子比特。

Image 56: 一个经过调整的图表,显示逻辑量子比特错误率与代码距离的关系,突出了随着代码距离的增加,逻辑错误的指数级抑制。

图 1 – 一个经过调整的图表,显示逻辑量子比特错误率与代码距离的关系,突出了随着代码距离的增加,逻辑错误的指数级抑制。该图说明了过渡到低于阈值性能以及使用距离为 7 的代码实现的“超越盈亏平衡”行为。(改编自 Google Quantum AI 的 [1\],CC BY 4.0)

101 个量子比特上的距离为 7 的表面代码有效地使逻辑量子比特的寿命(图 1c 中的蓝线)与未经校正的物理量子比特(图 1c 中的绿线)相比增加了一倍。这一成就表明,经过纠错的量子比特可以更长时间地保持相干性,这对于运行扩展的量子算法和计算至关重要。

控制工程视角:谷歌如何实现它

该实验不仅仅是对表面代码的测试,它还是一个精心策划的工程和控制壮举。控制系统必须在多个方面提供完美的精度——同步、频率控制、测量保真度、实时解码和稳定性——经过数小时的运行。让我们停下来谈谈其中一些有趣的挑战。

系统的核心是实时同步。每个校正周期都必须在 1.1 微秒内完成——这是一个测量量子比特的狭窄窗口。这种同步的精度对于防止错误累积和破坏计算的稳定性至关重要。实现这一点需要在量子比特阵列中精确协调控制信号,确保每个门操作、测量都完美对齐。

最重要的组件之一是实时解码。解码是指分析测量数据以确定错误发生的位置和方式的过程。为了使用逻辑量子比特执行通用量子计算,必须应用某些称为非克利福德门的门。应用这些门需要在实时解码的基础上实时纠正错误。在谷歌的系统中,实时解码器在运行超过一百万个校正周期时保持大约 63 微秒的恒定延迟。也就是说,实时纠错管道可以足够快地处理测量结果,以避免拥塞。这种快速解码过程至关重要,因为任何延迟都可能导致错误传播和累积,从而可能破坏逻辑量子比特的稳定性。

该实验还需要高保真门操作。量子比特门中的错误很容易在系统中传播,从而危及逻辑量子比特的稳定性。谷歌实现了低于 0.1% 的单量子比特门错误和大约 0.3% 的双量子比特 CZ 门错误——这些阈值对于保持逻辑量子比特随时间推移的稳定至关重要。为此,控制电子设备的高性能至关重要,因为保真度可能会直接因控制脉冲的错误而受损。当扩展表面代码时,这些保真度尤其重要,即使是微小的门错误也可能会降低纠错的有效性。

随着量子计算机扩展到更多量子比特和更长的计算,这些和更多的控制要求只会变得更加苛刻,这使得先进控制硬件的开发对于容错量子计算的未来至关重要。

在上述要求中,特别是实时解码是任何可扩展量子计算系统的基础,因为它提供了保持量子信息稳定所需的快速响应。

深入了解实时解码

谷歌的工作强调,解码的可行性取决于解码器的延迟和吞吐量,这是在阈值以下运行 QEC 的最重要的部分之一。

解码是一项经典的计算任务,可以在各种经典架构上有效地完成,例如 FPGA 或 GPU。然而,计算资源之间通常会存在权衡。例如,FPGA 的计算能力有限,但以确定性方式运行并具有严格的定时,使其适合管理量子比特控制和测量任务以及执行低延迟的专用经典计算。另一方面,CPU 或 GPU 可能会增加延迟,但可以进行更高级和更大的计算。在 Quantum Machines,我们与 NVIDIA 合作 提供了一个独特的平台,称为 DGX Quantum,它提供了超低控制器-解码器延迟、高性能计算能力和灵活的软件可编程性的独特组合。我们的平台(包括我们的控制器 OPX1000 和 CPU/GPU 之间小于 4 微秒的通信)允许轻松编程和执行 QEC 工作流程,包括实时解码,例如谷歌的解码。软件可编程性允许非常快速地迭代解码算法和方案。我们认为这是一个更快地实现可扩展和有效 QEC 的关键功能。事实是,需要进行更多的实验和基准测试,以了解使用哪些解码器、哪些经典资源优化性能并满足要求,以及如何设计最终可以在更大规模上运行 QEC 的系统。我们目前所知的是,对于 QEC 方案收敛,解码器的延迟应小于 10 微秒。观看我们的首席执行官 Itamar Sivan 进一步解释这一点,并以用于分解数字 21 的 Shor 算法为例。

DGX-quantum 已经上线,展示了控制器和 GPU 之间小于 4 微秒的往返延迟。要了解更多信息,请观看以下 IEEE QCE 2024 教程,该教程由 QM 和 NVIDIA 共同撰写,内容涉及 DGX-quantum。

Image 57: 视频教程:将 GPU 和 QPU 紧密集成在一起,用于量子纠错和最优控制。

视频教程:将 GPU 和 QPU 紧密集成在一起,用于量子纠错和最优控制。

那么,接下来是什么?

谷歌对低于阈值的量子纠错的演示标志着容错量子计算的一个里程碑。通过证明逻辑量子比特可以优于物理量子比特,并表明错误可以比它们累积得更快地纠正,他们为可扩展的量子处理器铺平了道路。

然而,这仅仅是个开始。未来,要使用经过纠错的逻辑量子比特执行通用量子计算,必须关闭完整的反馈回路,这意味着控制系统需要根据解码器计算实时做出决策。未来的发展将需要更快的解码器、更好的错误缓解策略、嵌入在量子程序中的自动校准以稳定参数,以及紧密集成和管理经典和量子工作流程的控制硬件。

谷歌的成就标志着朝着容错量子计算迈出了重要一步。通过证明逻辑错误率可以通过使用表面代码呈指数级抑制,这项工作为可靠的量子计算提供了一条可扩展且实用的途径。随着代码距离的增加,错误会快速减少,为能够以更高保真度处理复杂操作的量子处理器奠定了基础。此外,这种快速解码的实现代表了 QEC 的一项根本进步。这项技术允许比错误传播更快地纠正错误,从而最大限度地减少错误在量子系统中传播的机会。

量子纠错和容错愿景

实时、低延迟的反馈回路将成为未来容错量子设备的重要组成部分,以确保错误比它们累积得更快地得到纠正。这一原则在更广泛的量子计算社区中引起共鸣,在那里,快速而强大的控制机制被视为实现大规模、可靠量子操作的关键。

正如谷歌的里程碑式成就所表明的那样,通过专注于低延迟、高保真度的反馈和解码,更广泛的量子技术领域正在朝着容错量子计算的共同目标迈进。支持敏捷纠错和实时适应性的量子控制系统的发展将继续在追求稳定、可扩展的量子计算系统中发挥核心作用,这些系统可以部署在实际应用中。借助 DGX-quantum,我们才刚刚开始这段激动人心的旅程,敬请期待未来的发展!

Image 58: 由 NVIDIA 和 Quantum Machines 共同开发的 DGX-Quantum 解决方案支持大规模量子计算机的量子纠错 (QEC)、校准和快速重新调整。它利用经典资源(GPU 和 CPU)进行量子计算,具有低于 4 微秒的超快数据往返延迟。

由 NVIDIA 和 Quantum Machines 共同开发的 DGX Quantum 解决方案支持大规模量子计算机的量子纠错、校准和快速重新调整。它允许使用强大的经典资源(GPU 和 CPU)进行量子计算机操作,具有低于 4 微秒的超快数据往返延迟。

参考

[1] Acharya, Rajeev, et al. “量子纠错低于表面代码阈值。” arXiv preprint arXiv:2408.13687 (2024).

原文

bookmark-summary启发,制作了一套翻译流程,使用浏览器插件收藏文章后,自动翻译并提交pr到github仓库,流程如下:

翻译流程

流程

浏览器插件Memo

浏览器收藏页面,支持添加标签,可以根据标签执行不同的流程,需要翻译的文章我会在增加一个#translate标签

收藏网页

github actions

github actions会监听到浏览器插件的收藏事件(仓库中markdown文件会新增一行),然后调用dify workflow翻译文章,翻译结果会提交到github仓库

bookmark,markdown文件

dify workflow

使用jina抓取网页内容,返回markdown格式,在调用gpt翻译

dify,翻译

创建pr

使用github actions创建pr

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建pr
- name: pr blog
uses: peter-evans/create-pull-request@v7
with:
path: blog # 需要pr的repo位置
token: ${{ secrets.GH_TOKEN }} # github accesstoken
commit-message: 提交翻译到仓库
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>
signoff: false
branch: posts
delete-branch: true
title: "提交翻译到仓库"
body: |
新增翻译内容
- Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
labels: |
automated pr : translate
draft: false

注意

在一个github action 任务中checkout出2个仓库时有些需要注意的点,防止踩坑:

  • pr的目标仓库需要先设置允许访问(设置中打开 “Accessible from repositories owned by the user xxx”),否则会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
- name: Checkout bookmark
uses: actions/checkout@v4
with:
path: bookmark

# 需要再blog repo打开配置 Access-> Accessible from repositories owned by the user 'yiGmMk'
- name: Checkout blog
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
path: blog
repository: yiGmMk/blog
ref: master

效果

pr

工具

在这个流程中使用的工具:


直到现在,我才意识到并发和并行实际上是不同的概念,因为有些人经常互换使用它们。我在阅读《Clojure for the Brave and True》这本书的第 9 章时才了解到事实并非如此。

这让我想要更多地了解与并发和并行相关的概念,尤其是关于我最熟悉的编程语言:JavaScript。所以这篇文章基本上是我在这个学习过程中所做的笔记的集合。

顺序、并发和并行

在生活中执行任务时,我们以顺序、并发或并行的方式执行它们。这同样适用于计算。

顺序执行基本上是指任务一个接一个地完成,没有任何重叠。例如,如果一个人先看手机,完成手机上的工作,然后才切换到另一个任务,例如吃汤,他们就是在顺序工作。这种方法的问题是,有时你的任务会被阻塞,例如,当你用手机向朋友请求某件事时,如果你在朋友回答之前不切换到其他任务,你基本上会浪费时间。因此,不时地使用不同的多任务处理形式可以帮助节省时间。并发和并行是实现多任务处理的方法。但是,两者之间存在细微但重要的差异。

并发就像通过在子任务之间交替(也称为交错)来处理多个任务,而并行就像同时执行多个任务。例如,如果一个人看手机,放下手机舀一口汤,然后在放下勺子后回到手机,他们就是在并发工作。相反,如果一个人一边用一只手发短信,一边用另一只手吃饭,他们就是在并行工作。在这两种情况下,他们都在进行多任务处理,但他们在多任务处理的方式上存在细微的差异。

线程

在上面的类比中,我将吃汤和使用手机称为不同的任务,每个任务都由子任务组成(例如,吃汤,你需要拿着勺子,然后把它放到汤里,然后把它放到嘴里,等等……)。

同样,在编程的上下文中,子任务可以被认为是进程中较大的一组指令的各个段。同时操作不同子任务的传统方法是创建不同的内核线程。这些线程有点像单独的工作人员,每个工作人员处理他们特定的任务,同时能够处理同一组指令以及资源。

你的线程是并行运行还是并发运行实际上取决于你的硬件。如果你的 CPU 的核心数多于同时运行的线程数,则每个线程都可以分配给不同的核心,从而允许它们并行运行。但是,如果你的 CPU 的核心数少于线程数,则操作系统将开始在线程之间交错。

当涉及到内核线程时,开发人员的体验保持不变,无论任务是实际并发处理还是并行处理,都没有太大区别。开发人员使用线程来提高性能并避免阻塞。但是,操作系统会根据可用资源最终决定如何处理这些线程。只要开发人员使用线程,无论它们是并发运行还是并行运行都无关紧要;在这两种情况下,来自不同线程的指令的执行顺序都是不可预测的。因此,开发人员应警惕可能因两个不同线程操作相同数据而发生的潜在问题(如竞争条件、死锁、活锁等)!

生成进程、I/O 通知

除了使用线程之外,还有其他方法可以实现并发/并行,例如,虽然不如线程高效,但生成多个进程是另一种方法。由于 CPU 并行和并发地运行不同的进程,因此你可以使用多个进程进行多任务处理。这里的缺点是,每个进程都有其自己分配的内存空间,并且它们默认不共享它们的内存空间,就像线程一样。因此,如果你需要不同的进程在相同的状态下运行,你可能需要某种 IPC 机制,如共享内存段、管道、消息队列,甚至是数据库。

内核还实现了它们自己的 I/O 事件通知机制,这在构建你不想在执行某些任务时被阻塞的程序时也很有帮助。

我不想深入探讨太多细节,因为我对它了解不多,但关键思想是,内核线程不是你可以实现并发的唯一操作系统特定方法。

NodeJS,用户空间并发的示例

编程语言通常提供自己的并发机制,以简化与使用操作系统 API(系统调用)相关的复杂性。这意味着编译器或解释器可以将你的高级代码转换为操作系统理解的低级系统调用,这样你就不必考虑太多。

Node.js 就是这个概念的一个很好的例子。尽管你的 JavaScript 程序在单线程环境中以顺序执行流程运行,但诸如 IO 操作之类的阻塞任务会委托给 Node.js 工作线程。因此,NodeJS 在幕后使用线程来管理这些阻塞任务,而不会向开发人员透露管理它们的复杂性。

它的工作原理如下:阻塞操作,如写入文件或从文件读取,或发送网络请求,通常使用 Node.js 提供的内置函数来处理。当调用这些函数时,你通常会传递回调函数作为参数,以便 Node.js 工作线程在完成其任务时可以执行你提供的回调函数。

对 NodeJS 并发在幕后的工作原理有了一点了解后,我们现在可以开始通过检查某些情况/情境来实践这个理论。

考虑以下代码(感谢我的朋友 Onur 提出这个例子);

1
2
3
4
5
6
7
8
9
10
11
setTimeout(() => {
while (true) {
console.log("a");
}
}, 1000);

setTimeout(() => {
while (true) {
console.log("b");
}
}, 1000);

在这里,如果你运行这个程序,你屏幕上唯一会遇到的就是“a”s。这是因为 NodeJS 解释器会继续执行当前回调,只要还有可用的指令。

一旦主代码中的所有指令都执行完毕,NodeJS 运行时环境就会开始调用回调函数。你也可以将你编写的主代码视为默认被调用的回调。在上面的示例中,第一个 setTimeout 使用提供的回调函数执行,第二个 setTimeout 使用提供的回调函数执行。1 秒过后,它开始刷屏“a”s。你永远不会看到“b”s,因为一旦调用了第一个回调,它就会用它丑陋的 while 循环永远支配着主线程!因此,永远不会调用第二个回调。

这有一些重要的影响。首先,它减少了诸如竞争条件之类的问题的可能性,尽管它们仍然可能发生,尤其是与 C 之类的多线程语言相比。为什么?在类似 C 的语言中,CPU 在指令级别交错线程,而在这里,它主要发生在回调级别。只要你避免使用依赖于带有嵌套回调的 async 函数的复杂逻辑,就可以确定执行流程保持不中断,基本上是顺序的。

如果编程逻辑包含许多 基于异步回调的函数(如 fs.readFile()setTimeout()setImmediate(),甚至是 Promise.then()),则竞争条件很容易开始发生。

这也适用于 await 的使用,因为你可以将 await 语句视为将当前范围内的剩余代码包装到一个回调函数中的简写,该回调函数在等待的 Promise 被解析后运行。

考虑下面提供的 testtest2 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const
{scheduler} = require('node:timers/promises'),

test = async () => {
let x = 0

const foo = async () => {
let y = x
await scheduler.wait(100)
x = y + 1
}

await Promise.all([foo(), foo(), foo()])
console.log(x) // 返回 1,而不是 3
},

test2 = async () => {
let x = 0

const foo = async () => {
await scheduler.wait(100)
let y = x
x = y + 1
}

await Promise.all([foo(), foo(), foo()])
console.log(x) // 返回 3
},

main = () => {
test()
test2()
}

main()

test() 记录 1 的原因是,当调用 foo 函数时,它们一旦遇到 await scheduler.wait(100),它们就会立即完成。因为在幕后,使用 await scheduler.wait(100) 会评估如下内容:

1
2
3
scheduler.wait(100).then(() => {
x = y + 1
})

因此,第一个 foo 函数完成其工作,现在由回调函数继续执行业务,但由于它只会在 100 毫秒后被调用,因此 NodeJS 解释器不会闲置,而是继续按顺序执行第二个和第三个 foo 函数。它们还在来自第一个 foo 的回调被触发之前将 y 变量设置为 x 的值,并使用回调函数调用 scheduler.wait。因此,当回调最终被执行时,它们都使用 x 的先前值更新 x,因此我们得到 1,而不是 3。

为什么在运行 test2() 时会记录出 3?因为 await 运行的位置不同,它评估为如下内容:

1
2
3
4
scheduler.wait(100).then(() => {
let y = x
x = y + 1
})

一旦调用此回调函数,就没有任何东西可以在其间交错

因此,不会发生竞争条件。

总结

这里的核心思想是,实现“并发”的方法不止一种,你实现它的方式也会影响很多事情,比如你的程序的性能、你可能会遇到的问题,或者要注意哪些事情等等。

在处理应该并发/并行工作的程序时,请尽量注意。事情可能会很快出错。

附录

2024-09-18:这篇文章受到了我意想不到的关注。它在 HackerNews 上获得了 99 个赞,并在首页上出现了一段时间。几天后,我的朋友 Carlo 告诉我,我的文章被刊登在 Bytes 时事通讯的 第 323 期中,这是一份拥有超过 20 万订阅者的 JavaScript 时事通讯。

我还收到了几条表达对文章赞赏的信息,甚至收到了我博客 GitHub 存储库的第一个拉取请求。感谢每一位抽出时间阅读并提供反馈的人。

在 HackerNews 的讨论中,@duped 和 @donatj 推荐了 Rob Pike 的 并发不是并行。这是一个非常好的演讲,所以我也想在这里为任何对该主题进一步感兴趣的人提及它。

原文

以前,只有当页面的某一部分有ID时,我们才能链接到该部分。我们所需要做的就是链接到URL并添加文档片段(ID)。如果我们想链接到页面的某个部分,我们需要锚那部分链接到它。直到Text fragments在Chrome中实现

什么是Text fragments?

Text fragments是现代网页平台的一个强大功能,允许在不需要添加锚点的情况下,精确地链接到网页中的特定文本。这个功能由::target-text CSS 伪元素补充,提供了一种样式化高亮文本的方法。

Text fragments通过在 URL 的末尾追加特殊语法来工作,就像我们之前追加 ID 后面的哈希符号 (#) 一样。浏览器解释 URL 的这一部分,搜索页面上的指定文本,然后滚动到并高亮显示该文本(如果支持Text fragments)。如果用户尝试通过按 Tab 按钮导航文档,焦点将移动到Text fragments后的下一个可聚焦元素。

如何使用Text fragments?

以下是Text fragments URL 的基本语法:

1
https://example.com/page.html#:~:text=[prefix-,]textStart[,textEnd][,-suffix]

在哈希符号后面,我们添加特殊语法:~:,也称为片段指令,然后是text=,后面跟着:

  1. prefix-:一个文本字符串,前面有一个连字符,指定应该紧跟在链接文本之前的文本。这有助于浏览器在多个匹配项的情况下链接到正确的文本。这个部分没有高亮显示。
  2. textStart:要高亮显示的文本的开始。
  3. textEnd:要高亮显示的文本的结束。
  4. -suffix:一个连字符,后面跟着一个文本字符串,行为类似于前缀,但在文本之后出现。同样,在多个匹配项的情况下很有帮助,并且与链接文本一起高亮显示。

例如,以下链接:

1
https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments#:~:text=without%20relying%20on%20the%20presence%20of%20IDs

我们使用的Text fragments是“without relying on the presence of IDs”,但它是编码的。如果你跟随这个链接,它应该如下所示:
Image 14:Google Chrome 中高亮显示的Text fragments的屏幕截图

我们还可以通过设置startTextendText来高亮显示文本范围。考虑来自相同 URL 的以下示例:

1
https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments#:~:text=using%20particular,don't%20control

我们使用的Text fragments是“using particular”,后面跟着一个逗号,然后是“don’t control”。如果你跟随这个链接,它应该如下所示:
Image 15:Google Chrome 中显示高亮显示Text fragments的屏幕截图,包括起始文本和结束文本

我们还可以使用&符号来高亮显示多个文本。考虑以下内容:

1
https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments#:~:text=using%20particular&text=it%20allows

如果你跟随这个链接,它应该如下所示:
Image 16:Google Chrome 中显示不同高亮显示Text fragments的屏幕截图

Text fragments的一个有趣行为是,如果你链接到通过“查找页面”功能可发现的隐藏内容(例如,具有设置为“直到找到”的 hidden 属性的元素的子元素或关闭详细信息元素的内容),隐藏内容将变得可见。让我们通过链接到 Scott O’Hara 博客上的这篇文章来查看此行为。博客包含默认关闭的详细信息元素。

Image 17:Scott O'Hara 博客中显示详细信息部分的屏幕截图

如果我们链接到详细信息元素内部的Text fragments,它将自动打开:

1
https://www.scottohara.me/blog/2022/09/12/details-summary.html#:~:text=Oh%20hi%20there.%20Forget%20your%20summary,%20didja

Image 18:Scott O'Hara 博客中显示详细信息部分的屏幕截图,并且由于它与详细信息部分内部的Text fragments匹配,因此它会打开

注意此行为仅在 Google Chrome 中可用,因为它是唯一支持可发现内容的浏览器。

样式化高亮片段

如果浏览器支持Text fragments,我们可以使用:target-text伪元素来更改高亮文本的样式

1
2
3
::target-text {
background-color: yellow;
}

请注意,我们只允许更改以下属性:

  • 颜色
  • 背景颜色
  • 文本装饰和相关属性(包括文本下划线位置和文本下划线偏移)
  • 文本阴影
  • 笔划颜色、填充颜色和笔划宽度
  • 自定义属性

浏览器支持和回退行为

Text fragments目前在所有浏览器中都受支持。:target-text伪元素尚不支持 Safari,但现在可以在 Technology Preview 版本中使用。如果浏览器不支持此功能,它将优雅地降级,页面将在不高亮显示或滚动到文本的情况下加载。

默认的高亮样式因浏览器而异。高亮颜色在不同的浏览器中有所不同。Safari 中的高亮区域更大,跨越整个行高。在 Firefox 和 Chrome 中,只有文本才会高亮显示,行之间的空间是空白的。

Image 19:显示不同浏览器之间文本高亮显示差异的屏幕截图

我们可以使用document.fragmentDirective检测功能是否受支持。它将返回一个空的 FragmentDirective 对象,如果支持;否则返回 undefined。

结论

我第一次遇到Text fragments是通过 Google 搜索结果生成的链接。最初,我以为这是一个 Chrome 特有的功能,而不是更广泛的 web 标准的一部分。然而,我很快就意识到这个功能实际上是建立在开放的 web 上的,可以供任何选择实现它的浏览器使用。

我希望看到这个功能被更广泛地使用,特别是由负责的生成 AI 系统。想象一下 AI 可以提供直接、上下文敏感的链接到您感兴趣的确切内容,使用Text fragments进行精确引用。这不仅可以增加透明度,还可以改善用户在浏览 AI 生成的内容时的体验。

展望未来,如果Text fragments对所有用户都更易于访问,而不仅仅是那些具有技术知识的人,那将是一件很棒的事情。要是浏览器提供了内置功能,使非技术用户能够轻松地高亮显示文本并生成特定段落的链接就好了?这可以通过本地浏览器功能或甚至简单的浏览器扩展来实现——无论哪种方式,都可以让深层链接对每个人来说都是一种享受。

最后,我要向 Hannah Olukoye 和 Jens Oliver Meiert 表达我最诚挚的感谢,因为他们花时间分享了他们无价的反馈和更正。

更新,2024 年 10 月 20 日

事实证明,生成链接到特定Text fragments的能力已经内置于基于 Chromium 的浏览器中,如 Hostam Sultan 在 X(以前的 Twitter)上澄清的那样。如果您使用的是 Chrome,只需高亮显示一些文本,右键单击,然后在上下文菜单中找到“复制链接到高亮显示”选项。

附加资源

使用gpt4o翻译,提示词:

1
2
3
4
你是一个优秀的翻译官,请将下面的内容翻译成中文
1. 仅返回译文
2. 不要改变内容格式,只做翻译
3. 返回可复制的markdown内容,如列表请返回"- xxx",并用````包围起来

英文版

1
2
3
4
You are an excellent translator, please translate the following into Chinese
1. Return to translation only
2. Don't change the content format, just translate
3. Returns copyable markdown content, such as lists, return "- xxx" and surround it with ```.

要返回markdown格式,需要用这个提示词明确要求才能正常工作

返回可复制的markdown内容,如列表请返回”- xxx”,并用````包围起来

结果如下:

正常工作的提示词,效果

没有该要求时效果:

不正常的提示词,效果

如何写优质的Prompt

需要遵循两个原则:

原则一:编写清晰、具体的指令

策略1:使用合理的分隔符,更清晰地表示输入的不同部分。
策略2:给模型指示,要求结构化地输出内容。
策略3:可以要求模型检查是否满足条件。
策略4:可以给模型提供少量示例,以获得更优的结果。

原则二:给模型可以思考的时间

策略5:给模型指定完成任务所需的步骤。
策略6:引导模型在下结论之前找出一个自己的解法。

合理使用分隔符

分隔符可以是任何标记,只要让模型明确知道这是一个单独的部分即可。

1
2
3
4
5
6
7
8
9
10
11
12
# 原始的内容
text = f"""
  你应该提供尽可能清晰、具体的指示,以表达你希望模型执行的任务。\
  这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。\
  不要将写清晰的提示与写简短的提示混淆。\
  在许多情况下,更长的提示可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。"""

# 通过分隔符要求ChatGPT总结分隔符包含的内容。
prompt = f"""
  把用三个反引号包裹起来的文本内容总结成一句话。
  ```{text}```
  """

格式化输出内容

要求ChatGPT以某种格式化的方式输出内容,这样的输出更容易用程序解析

1
2
3
4
5
6
prompt = """
请生成3本虚拟的书,包括书名、作者、出版社等信息。\
并以json形式输出结果,其中包括以下json key:\
book_name, author, publisher\
"""
# 输出: [ {    "book_name": "时光之旅",    "author": "张三",    "publisher": "人民文学出版社" }, {    "book_name": "未来的世界",    "author": "李四",    "publisher": "科学出版社" }, {    "book_name": "爱情的故事",    "author": "王五",    "publisher": "上海文艺出版社" }]

提供少量示例,要求模型按示例格式输出结果

1
2
3
4
5
6
prompt = f"""
  你的任务是以一致的风格来回答问题。
  <学生>:教我耐心。\
  <老师>:挖出最深峡谷的河流源于一处不起眼的泉眼;最宏伟的交响乐从单一的音符开始;最复杂的挂毯以一根孤独的线开始编织。\
  <学生>:教我坚韧。
  """

给模型指定完成任务所需的步骤

如果模型匆忙得出了错误的结论,您可以尝试重新构思查询,请求模型在提供最终答案之前进行一系列相关的推理。
在这些情况下,可以指示模型花更多时间思考问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
text = f"""
  在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。\
  他们一边唱着欢乐的歌,一边往上爬,\
  然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。\
  虽然略有些摔伤,但他们还是回到了温馨的家中。\
  尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。
"""

prompt_1 = f"""
  执行以下操作:
  1、用一句话概括下面用三个反引号括起来的文本。
  2、将摘要翻译成英语。
  3、在英语摘要中列出每个人名。
  4、输出一个 JSON 对象,其中包含以下键:English_summary,num_names。

  请用换行符分隔您的答案。

  Text:
  ```{text}```
  """

输出:

输出1

优化Prompt,及优化后的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
text = f"""
  在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。\
  他们一边唱着欢乐的歌,一边往上爬,\
  然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。\
  虽然略有些摔伤,但他们还是回到了温馨的家中。\
  尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。
  """

prompt_2 = f"""
  执行以下操作:
  1、用一句话概括下面用<>括起来的文本。
  2、将摘要翻译成英语。
  3、在英语摘要中列出每个人名。
  4、输出一个 JSON 对象,其中包含以下键:English_summary,num_names。

  请使用以下格式:
  文本:<要总结的文本>
  摘要:<摘要>
  翻译:<摘要的翻译>
  名称:<英语摘要中的名称列表>
  输出 JSON:<带有 English_summary 和 num_names 的 JSON>

  Text:
  ```{text}```
  """

效果:

1
2
3
4
5
6
7
8
9
文本:<在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。他们一边唱着欢乐的歌,一边往上爬,然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。虽然略有些摔伤,但他们还是回到了温馨的家中。尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。>

摘要:杰克与吉尔在打水过程中摔倒但安全回家,并继续冒险。

翻译:Jack and Jill fell while fetching water but returned home safely and continued their adventures.

名称:[Jack, Jill]

输出 JSON:{"English_summary": "Jack and Jill fell while fetching water but returned home safely and continued their adventures.", "num_names": 2}

引导模型在下结论之前找出一个自己的解法

有时候,我们让模型直接判断我们的输入是否正确,模型可能会得到错误的结果。
这时候我们可以指示模型,不要直接判断我们的输入,而是先让模型自行解答,再通过对比两个结果,来判断我们的输入是否正确。
接下来我们会给出一个问题和一个学生的解答,要求模型判断解答是否正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
prompt = f"""
  判断学生的解决方案是否正确。

  问题:
  我正在建造一个太阳能发电站,需要帮助计算财务。
  土地费用:每平方英尺100美元
  太阳能电池板:每平方英尺250美元
  维护费用:每年需要支付固定的100000美元,并额外支付每平方英尺10美元
  作为平方英尺数的函数,首年运营的总费用是多少。

  学生的解决方案:
  设x为发电站的大小,单位为平方英尺。
  费用:
  土地费用:100*x
  太阳能电池板费用:250*x
  维护费用:100000+100*x
  总费用:100*x+250*x+100000+100*x=450*x+100000
  """

输出:

错误的结果

优化Prompt:
可以通过指导模型先自行找出一个解法来解决这个问题。
在接下来这个 Prompt 中,我们要求模型先自行解决这个问题,再根据自己的解法与学生的解法进行对比,从而判断学生的解法是否正确。
同时,我们给定了输出的格式要求。通过明确步骤,让模型有更多时间思考,有时可以获得更准确的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
prompt = f"""
  请判断学生的解决方案是否正确,请通过如下步骤解决这个问题:
  步骤:
  首先,自己解决问题。
  然后将你的解决方案与学生的解决方案进行比较,并评估学生的解决方案是否正确。
  在自己完成问题之前,请勿决定学生的解决方案是否正确。

  使用以下格式:
  问题:问题文本
  学生的解决方案:学生的解决方案文本
  实际解决方案和步骤:实际解决方案和步骤文本
  学生的解决方案和实际解决方案是否相同:是或否
  学生的成绩:正确或不正确

  问题:
      我正在建造一个太阳能发电站,需要帮助计算财务。
      - 土地费用为每平方英尺100美元
      - 购买太阳能电池板每平方英尺250美元
      - 每年需要支付固定的100000美元,并额外支付每平方英尺10美元
      作为平方英尺数的函数,首年运营的总费用是多少?

  学生的解决方案:
      设x为发电站的大小,单位为平方英尺。
      费用:
      1. 土地费用:100*x
      2. 太阳能电池板费用:250*x
      3. 维护费用:100000+100*x
      总费用:100*x+250*x+100000+100*x=450*x+100000

  实际解决方案和步骤:
  """

输出:

优化后的结果

国产模型对比

以下模型来自硅基流动部署

阅读全文 »

每个软件开发者都应该知道的技术事项的集合。

  • 具有强烈的个人见解。不以科学为依据。
  • 没有特定的顺序
  • 为成为程序员,你不需要知道所有这些。但是了解这些内容将帮助你变得更好!

介绍

谬误

  • 精彩谬误
    💊 程序员相信的谬误的精选列表。
    查看你不了解的字符串、地址、名称、数字、电子邮件、时区和日期等内容。

算法

数据结构

数字

字符串

延迟

时间

内存

分布式系统

正则表达式

安全

用户体验/可用性

搜索引擎优化

架构

代码设计

工程哲学

实践

职业

微调你的简历

开源

远程工作

问题解决

软技能

心理健康

编程论文

免费编程书籍

服务 ⚡

授权

哪里可以查找更多信息

编码练习网站 ⚡

原文

使用gpt4o翻译,提示词:

1
2
3
4
你是一个优秀的翻译官,请将下面的内容翻译成中文
1. 仅返回译文
2. 不要改变内容格式,只做翻译
3. 返回可复制的markdown内容,如列表请返回"- xxx",并用````包围起来

英文版

1
2
3
4
You are an excellent translator, please translate the following into Chinese
1. Return to translation only
2. Don't change the content format, just translate
3. Returns copyable markdown content, such as lists, return "- xxx" and surround it with ```.

简介

在Go语言中,fmt包提供了多种格式化输出的方法,其中Stringer接口用于生成自定义类型的字符串表示。fmt包中的Stringer接口定义如下:

1
2
3
4
type Stringer interface {
String() string
}
// fmt.Println fmt.Print生效

然而,fmt包还提供了一个更具体的接口GoStringer,用于生成Go语言的字符串表示,这在调试和日志记录中非常有用。GoStringer接口定义如下:

1
2
3
4
type GoStringer interface {
GoString() string
}
// fmt.Printf("%#v\n",x) 生效

核心概念和功能

GoStringer接口允许你为自定义类型定义一个方法,该方法返回一个Go语言的字符串表示。这在调试时非常有用,因为它可以提供关于对象的更详细的信息。

示例代码

假设我们有一个自定义类型Person,我们希望它能够提供一个Go语言的字符串表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p Person) GoString() string {
return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age)
}

func main() {
p := Person{"Alice", 30}
fmt.Println(p) // 输出: {Alice 30}
fmt.Printf("%#v\n", p) // 输出: Person{Name: "Alice", Age: 30}
}

在这个例子中,我们定义了一个Person类型,并实现了GoStringer接口。当我们打印Person类型的实例时,它会调用GoString方法,返回一个Go语言的字符串表示。

Go语言的独特特性或优势

使用GoStringer接口的一个主要优势是它提供了更详细的字符串表示,这对于调试和日志记录非常有用。此外,它还允许你为自定义类型定义一个标准的字符串表示,这在与其他Go语言代码集成时非常有用。

完整示例

下面是一个更复杂的例子,展示了如何为一个自定义类型实现GoStringer接口,并在调试时使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string
Age int
}

func (p Person) GoString() string {
return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age)
}

func main() {
p := Person{"Alice", 30}
fmt.Printf("%#v\n", p) // 输出: Person{Name: "Alice", Age: 30}
// fmt.Printf("%#v" 默认输出会携带包名,如 main.Person{Name: "Alice", Age: 30} )
// 使用reflect包来展示GoStringer接口的使用
v := reflect.ValueOf(p)
if gs, ok := v.Interface().(fmt.GoStringer); ok {
fmt.Println(gs.GoString()) // 输出: Person{Name: "Alice", Age: 30}
}
}

总结

通过实现GoStringer接口,你可以为自定义类型定义一个Go语言的字符串表示,这在调试和日志记录中非常有用。此外,它还允许你为自定义类型定义一个标准的字符串表示,这在与其他Go语言代码集成时非常有用。

在使用GoStringer接口时,需要注意的是,它返回的字符串表示应该尽可能详细,以便在调试时提供足够的信息。此外,你还可以考虑实现Stringer接口,以便在不需要Go语言的字符串表示时提供一个更简洁的字符串表示。

服务器端渲染(SSR)已经存在了一段时间,但仍然值得我们深入探讨。这项技术可以让你的应用更快、对搜索引擎更友好。在本指南中,我们将解释什么是 SSR,为什么你可能想要使用它,以及如何在不让你抓狂的情况下实现它。我们将涵盖基础知识,将其与客户端渲染进行比较,并讨论一些实际示例。

什么是服务器端渲染?

从根本上说,SSR 是关于在服务器上渲染你的网页,而不是在浏览器中。当用户请求一个页面时,服务器会完成所有繁重的工作,并将一个完全渲染的页面发送给客户端。然后,客户端的 JavaScript 接管,使其具有交互性。服务器在厨房里做准备工作,而浏览器只需将菜端上桌。
以下是一个最小的 Express.js 示例:

SSR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const App = require('./App');

const app = express();

app.get('/', (req, res) => {
const html = ReactDOMServer.renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html>
<body>
<div id="root">${html}</div>
<script src="client.js"></script>
</body>
</html>
`);
});

app.listen(3000, () => console.log('Server running on port 3000'));

从服务器到浏览器,完全渲染的页面

当我们谈论 SSR 交付“完全渲染的页面”时,理解其真正含义非常重要。让我们来分解一下:

什么是完全渲染的页面?

完全渲染的页面是一个包含用户首次加载页面时会获得的所有内容的 HTML 文档。这包括:

  • 完整的 DOM 结构
  • 所有文本内容
  • 图像占位符和其他媒体元素
  • 初始样式

以下是一个基本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<title>My SSR Page</title>
<style>
/* Initial styles */
</style>
</head>
<body>
<header>
<h1>Welcome to My Site</h1>
<nav><!-- Fully populated navigation --></nav>
</header>
<main>
<article>
<h2>Article Title</h2>
<p>This is the full content of the article...</p>
</article>
</main>
<footer><!-- Fully populated footer --></footer>
<script src="hydration.js"></script>
</body>
</html>

与客户端渲染(CSR)的区别

相比之下,一个客户端渲染(CSR)的初始 HTML 可能如下所示:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>My CSR Page</title>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>

CSR 页面完全依赖 JavaScript 来填充内容。

完全渲染的 HTML 的好处

  • 更快的初始绘制:浏览器可以立即开始渲染内容。
  • 更好的 SEO:搜索引擎可以读取所有内容,而无需执行 JavaScript。
  • 改进的可访问性:屏幕阅读器和其他辅助技术可以立即访问内容。
  • 韧性:即使 JavaScript 加载失败,基本内容仍然可用。

The hydration process

在发送完全渲染的 HTML 之后,SSR 应用程序通常会经历一个称为 hydration 的过程:

  • 服务器发送完全渲染的 HTML。
  • 浏览器立即显示此 HTML。
  • JavaScript 加载并 hydrates 页面,添加交互性。
    以下是一个简化的 React 水合示例:
    1
    2
    3
    4
    5
    6
    // Simplified React hydration example
    import { hydrateRoot } from 'react-dom/client';
    import App from './App';

    const domNode = document.getElementById('root');
    hydrateRoot(domNode, <App />);
    这个过程允许快速的初始加载,同时仍然提供现代 Web 应用的丰富交互性。
    请记住,虽然 SSR 提供了这些完全渲染的页面,但它并非没有权衡。服务器的工作量更大,你需要在服务器和客户端之间小心处理状态。然而,对于许多应用来说,完全渲染页面的好处使 SSR 成为一个有吸引力的选择。

CSR 和 SSR 有什么区别?

客户端渲染(CSR)和服务器端渲染(SSR)是渲染网页的两种不同方法。以下是它们的主要区别:

客户端渲染(CSR)

  1. 服务器发送一个包含 JavaScript 包的最小 HTML 文件。
  2. 浏览器下载并运行 JavaScript。
  3. JavaScript 创建页面内容并使其具有交互性。

    优点:

  • 初始加载后平滑的交互
  • 需要更少的服务器资源

    缺点:

  • 较慢的初始页面加载
  • 潜在的 SEO 挑战

服务器端渲染(SSR)

  1. 服务器创建完整的 HTML 内容。
  2. 浏览器快速接收并显示预渲染的 HTML。
  3. 然后加载 JavaScript 以使页面完全具有交互性。

    优点:

  • 更快的初始页面加载
  • 更适合 SEO
  • 在较慢的设备上表现良好

    缺点:

  • 设置可能更复杂
  • 可能使用更多的服务器资源

CSR VS SSR

从本质上讲,CSR更多地在浏览器中工作,而SSR更多地在服务器上工作。它们之间的选择取决于项目的特定需求,平衡因素,如初始加载时间,SEO要求和服务器资源。

SSR 和搜索引擎:HTTP 中的完美匹配

服务器端渲染可以对搜索引擎如何看待你的网站产生重大影响。让我们来分解一下:

更快的索引

搜索引擎机器人是没有耐心的。它们希望立即看到你的内容。使用 SSR,当机器人来敲门时,你的页面已经准备好了——不需要等待 JavaScript 加载和渲染。

内容一致性

SSR 确保搜索引擎看到与用户相同的内容。使用客户端渲染,总是存在机器人可能错过一些动态加载内容的风险。

改进的加载时间

搜索引擎喜欢快速的网站。SSR 可以显著减少初始加载时间,这可能在排名中给你带来一些优势。

移动优先索引

随着谷歌的移动优先索引,SSR 在较慢的移动连接上的性能优势变得更加重要。

预览

虽然严格来说不是搜索引擎功能,但 SSR 使在社交平台上共享内容时生成准确的预览变得更加容易。这可以通过增加参与度和反向链接来间接提升你的 SEO。
SSR 是 SEO 的强大工具,但它不是唯一因素。内容质量、相关性和整体用户体验在搜索引擎排名中至关重要。SSR 只是确保搜索引擎可以有效地抓取和索引你的内容,潜在地在可见性和性能指标上给你带来优势。

如何实际实现 SSR

实现 SSR 并不需要很复杂。让我们来看看如何使用 Next.js(一个流行的 React 框架,使 SSR 变得简单)来实现它:

  1. 设置一个 Next.js 项目。
  2. 创建服务器端渲染的页面。
  3. 让 Next.js 处理完全渲染的 HTML 和客户端水合。

以下是一个使用 App Router 的简单 Next.js 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/page.js
async function getData() {
const res = await fetch('<https://api.example.com/data>')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}

export default async function Home() {
const data = await getData()

return <h1>Hello {data.name}</h1>
}

在这个示例中:

  1. Home 组件是一个异步函数,允许服务器端数据获取。
  2. getData() 获取我们需要的数据。
  3. 组件直接渲染数据。

Next.js 自动处理 SSR 过程:

  1. 当请求进来时,Next.js 在服务器上运行这个组件。
  2. 它等待数据被获取。
  3. 它使用获取的数据渲染组件。
  4. 完全渲染的 HTML 被发送到客户端。
  5. 一旦浏览器中加载了 JavaScript,页面就变得具有交互性。

这种方法让你在不需要手动设置服务器或管理渲染过程的情况下获得 SSR 的好处。

更高级的 SSR 解决方案

如果你不想重新发明轮子,有几个框架可以为你处理 SSR 的复杂性。以下是不同生态系统中流行选项

选项

参考

在Windows系统,AppData,是一个包含三个子文件夹的文件夹:Local、LocalLow 和 Roaming。
了解不同类型的 AppData 文件夹及其用途可以帮助你进行故障排除、管理存储空间等。

AppData

当在 Windows 上安装程序时,它通常会被安装到 C:\Program Files 或 C:\Program Files (x86)(如果是 32 位程序)。该应用程序将为计算机上的所有用户安装,并需要管理员权限才能写入。存储在该文件夹中的任何应用程序设置由所有用户共享。这就是 AppData 的作用。
它是一个隐藏的文件夹,位于每个用户文件夹下。它位于 C:\Users<username>\AppData,包含可能与程序运行能力无关的程序特定信息,
如用户配置。在你的 AppData 文件夹中,你会找到像这样的文件:

  • 用户特定的安装
  • 应用程序配置文件
  • 缓存文件
    如果程序在安装时询问你是否要为所有用户安装的程序,它基本上是在询问你是否要将其安装到 Program Files 或 AppData。Python就是
    这样。此外,AppData 中有三个子文件夹,它们之间的区别很重要。

如何快速找到这些文件夹

  1. 键盘按下”Windows” + “R” 键,会弹出运行窗口
  2. 输入 %appdata% 或 %localappdata% 可以找到用户文件下的appdata

%localappdata% 将打开 C:\Users\你的用户名\AppData\Local 目录
%appdata% 将打开 C:\Users\你的用户名\AppData\Roaming 目录

什么是 Local

Local 文件夹用于存储无法从你的用户配置文件中移动的文件,也经常包含可能太大而无法与服务器同步的文件。例如,它可能存储一些视频游戏
运行所需的文件或你的网络浏览器缓存,这些文件可能太大或没有意义转移到其他地方。开发人员还可能使用 Local 来存储与此特定计算机上的
文件路径相关的信息。将这些配置文件移动到另一台计算机可能会导致程序停止工作,因为文件路径不匹配。其他存储在这里的文件往往是日志文
件、临时文件或非必要数据。

什么是 LocalLow

LocalLow 与 Local 非常相似,但名称中的 “low” 指的是授予应用程序的较低访问级别。例如,以隐身模式运行的浏览器可能仅限于只能访问
LocalLow 文件夹,以防止它能够访问存储在 Local 中的正常用户数据。基本上,这是针对以更受限的安全权限运行的应用程序的。

什么是 Roaming

如果你在域(domain)(a network of computers with a central domain controller that handles your login)上使用 Windows 计算机,那么你可能熟悉 Roaming 文件夹。
如果在同一个域上登录其他设备,此文件夹中的文件将被同步,因为它们被认为是使用你的设备所必需的。这可能是你的网络浏览器收藏夹和书
签、重要的应用程序设置等。当存储的数据可以在设备之间移动而不会出现任何问题时,建议使用此文件夹。例如,《我的世界》将其世界文件、
截图等存储在 Roaming 文件夹中,因为这些文件都可以被获取并迁移到新设备,并且预计可以正常工作。Roaming 非常适合企业环境,包括
Outlook 配置文件和网络打印机配置等设置。它通过存储用户特定的设置和文件,帮助在网络中的不同计算机之间为用户带来一致的环境。

原文

What is AppData, and what are Local, LocalLow, and Roaming?

以下摘自对问题”永生是否是一种酷刑”的回答:

思维是指导行为的。小时候曾经有过一次,因上课不听话被老师叫到走廊罚站了整日。

到了晚上奶奶来接我,我说今日站在走廊经历同学走过议论,太丢脸了,不想再去学校了。奶奶笑着告诉我,现在我看是天大的事情,但等长大一些,今天不过是记忆中的一件小事罢了。

“永生是否是一种酷刑”,很像现代年轻人偶尔会陷入的悲观焦虑。有时候一件小事,在当时看来,是足以毁灭自己的,前途没了光明。其实,等你走到未来,发现还有无限可能。

前几天安慰一位妈妈,她说知道不对,但放不下对孩子每次考试成绩的焦虑。

我劝她不妨试着将目标放大,将结果放小去看,或许有用。

目标放大,想象孩子25岁成年工作、结婚的样子。孩子的这一次考试还是否还那么重要?

结果放小,体会你与孩子的每一次学习、玩耍的瞬间,他是不是努力、快乐、感到幸福?

如果你也有不开心的时候,感觉生命无法迈过的时刻,不妨也想象在更长的时间里,在宇宙的维度下,这烦恼不过是一瞬的烟云。认真把握每个精彩的时刻,过好每一个陪伴亲人的瞬间。

不要为没发生的事情而烦恼。“不悔既往,不负当下,不惧未来”,宇宙的安排都是完美的。

类似的句子

  • “The best way to predict the future is to create it.” - Peter Drucker
  • “In the end, we only regret the chances we didn’t take.” - Lewis Carroll
  • “Life is 10% what happens to us and 90% how we react to it.” - Charles R. Swindoll
  • “Worry does not empty tomorrow of its sorrow, it empties today of its strength.” - Corrie ten Boom
  • “The only thing we have to fear is fear itself.” - Franklin D. Roosevelt
  • “千里之行,始于足下。” - 老子
  • “路遥知马力,日久见人心。” - 《元曲选·争报恩》

原文

永生是否是一种酷刑? - 也说的回答: 不要用非永生的大脑,去思考永生后的烦恼。