or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

edit-operations.mdindex.mdstring-averaging.mdstring-distance.md

edit-operations.mddocs/

0

# Edit Operations

1

2

Functions for analyzing and manipulating edit operation sequences that transform one string into another. These functions provide detailed analysis of the specific changes needed and support conversion between different operation formats.

3

4

## Capabilities

5

6

### Edit Operations (Triples)

7

8

Find sequence of edit operations transforming one string to another, returning operations as triples (operation, source_pos, dest_pos).

9

10

```python { .api }

11

def editops(*args):

12

"""

13

Find sequence of edit operations transforming one string to another.

14

15

Two calling patterns:

16

- editops(source_string, destination_string): Find operations

17

- editops(opcodes, source_length, destination_length): Convert from opcodes

18

19

Parameters:

20

- source_string: Source string or opcodes list for conversion

21

- destination_string: Destination string or source length for conversion

22

- (conversion mode): destination_length for conversion

23

24

Returns:

25

list: List of tuples (operation, source_pos, dest_pos) where:

26

- operation: 'delete', 'insert', or 'replace'

27

- source_pos: Position in source string

28

- dest_pos: Position in destination string

29

"""

30

```

31

32

**Usage Examples:**

33

34

```python

35

import Levenshtein

36

37

# Find edit operations between strings

38

ops = Levenshtein.editops("spam", "park")

39

print(ops) # [('delete', 0, 0), ('insert', 3, 2), ('replace', 3, 3)]

40

41

# Convert from opcodes to editops

42

opcodes_list = [('delete', 0, 1, 0, 0), ('equal', 1, 3, 0, 2),

43

('insert', 3, 3, 2, 3), ('replace', 3, 4, 3, 4)]

44

editops_list = Levenshtein.editops(opcodes_list, len("spam"), len("park"))

45

print(editops_list) # Converted edit operations

46

```

47

48

### Opcodes (5-tuples)

49

50

Find sequence of edit operations in SequenceMatcher-compatible format, returning 5-tuples that include range information.

51

52

```python { .api }

53

def opcodes(*args):

54

"""

55

Find sequence of edit operations in SequenceMatcher format.

56

57

Two calling patterns:

58

- opcodes(source_string, destination_string): Find operations

59

- opcodes(editops, source_length, destination_length): Convert from editops

60

61

Parameters:

62

- source_string: Source string or editops list for conversion

63

- destination_string: Destination string or source length for conversion

64

- (conversion mode): destination_length for conversion

65

66

Returns:

67

list: List of 5-tuples (operation, start1, end1, start2, end2) where:

68

- operation: 'delete', 'insert', 'replace', or 'equal'

69

- start1, end1: Range in source string

70

- start2, end2: Range in destination string

71

"""

72

```

73

74

**Usage Examples:**

75

76

```python

77

import Levenshtein

78

79

# Find opcodes between strings

80

ops = Levenshtein.opcodes("spam", "park")

81

for op in ops:

82

print(op)

83

# Output:

84

# ('delete', 0, 1, 0, 0)

85

# ('equal', 1, 3, 0, 2)

86

# ('insert', 3, 3, 2, 3)

87

# ('replace', 3, 4, 3, 4)

88

89

# Convert from editops to opcodes

90

editops_list = [('delete', 0, 0), ('insert', 3, 2), ('replace', 3, 3)]

91

opcodes_list = Levenshtein.opcodes(editops_list, len("spam"), len("park"))

92

print(opcodes_list) # Converted opcodes

93

```

94

95

### Matching Blocks

96

97

Find identical blocks in two strings from edit operations, compatible with SequenceMatcher's get_matching_blocks() output.

98

99

```python { .api }

100

def matching_blocks(edit_operations, source_string, destination_string):

101

"""

102

Find identical blocks in two strings from edit operations.

103

104

Parameters:

105

- edit_operations: List of editops or opcodes

106

- source_string: Source string or its length as int

107

- destination_string: Destination string or its length as int

108

109

Returns:

110

list: List of triples (source_pos, dest_pos, length) representing matching blocks.

111

Always ends with a zero-length block for compatibility.

112

"""

113

```

114

115

**Usage Examples:**

116

117

```python

118

import Levenshtein

119

120

# Get matching blocks from edit operations

121

a, b = "spam", "park"

122

ops = Levenshtein.editops(a, b)

123

blocks = Levenshtein.matching_blocks(ops, a, b)

124

print(blocks) # [(1, 0, 2), (4, 4, 0)]

125

126

# Works with string lengths too

127

blocks = Levenshtein.matching_blocks(ops, len(a), len(b))

128

print(blocks) # Same result

129

130

# Extract matching substrings

131

a, b = "dog kennels", "mattresses"

132

ops = Levenshtein.editops(a, b)

133

blocks = Levenshtein.matching_blocks(ops, a, b)

134

matches_a = ''.join([a[block[0]:block[0]+block[2]] for block in blocks])

135

matches_b = ''.join([b[block[1]:block[1]+block[2]] for block in blocks])

136

print(f"Matching parts: '{matches_a}' == '{matches_b}'") # 'ees' == 'ees'

137

```

138

139

### Apply Edit Operations

140

141

Apply a sequence of edit operations to transform a string, supporting both complete and partial operation sequences.

142

143

```python { .api }

144

def apply_edit(edit_operations, source_string, destination_string):

145

"""

146

Apply sequence of edit operations to transform a string.

147

148

Parameters:

149

- edit_operations: List of editops or opcodes to apply

150

- source_string: Source string to transform

151

- destination_string: Destination string (for reference/validation)

152

153

Returns:

154

str: Transformed string after applying operations

155

"""

156

```

157

158

**Usage Examples:**

159

160

```python

161

import Levenshtein

162

163

# Apply complete edit sequence

164

source = "man"

165

dest = "scotsman"

166

ops = Levenshtein.editops(source, dest)

167

result = Levenshtein.apply_edit(ops, source, dest)

168

print(f"'{source}' -> '{result}'") # 'man' -> 'scotsman'

169

170

# Apply partial edit sequence

171

partial_ops = ops[:3] # First 3 operations only

172

partial_result = Levenshtein.apply_edit(partial_ops, source, dest)

173

print(f"Partial: '{source}' -> '{partial_result}'") # 'man' -> 'scoman'

174

175

# Works with opcodes too

176

opcodes_list = Levenshtein.opcodes(source, dest)

177

result = Levenshtein.apply_edit(opcodes_list, source, dest)

178

print(f"With opcodes: '{source}' -> '{result}'") # 'man' -> 'scotsman'

179

```

180

181

### Subtract Edit Operations

182

183

Subtract an edit subsequence from a sequence, creating operations that complete the transformation after a partial application.

184

185

```python { .api }

186

def subtract_edit(edit_operations, subsequence):

187

"""

188

Subtract an edit subsequence from an operation sequence.

189

190

Parameters:

191

- edit_operations: Complete list of edit operations

192

- subsequence: Ordered subset of operations to subtract

193

194

Returns:

195

list: Remaining operations after subtracting the subsequence

196

197

Note: Only works with editops (triples), not opcodes

198

"""

199

```

200

201

**Usage Examples:**

202

203

```python

204

import Levenshtein

205

206

# Get complete edit sequence

207

source = "man"

208

dest = "scotsman"

209

complete_ops = Levenshtein.editops(source, dest)

210

print("Complete ops:", complete_ops)

211

212

# Apply partial operations

213

partial_ops = complete_ops[:3]

214

intermediate = Levenshtein.apply_edit(partial_ops, source, dest)

215

print(f"After partial: '{intermediate}'")

216

217

# Calculate remaining operations

218

remaining_ops = Levenshtein.subtract_edit(complete_ops, partial_ops)

219

print("Remaining ops:", remaining_ops)

220

221

# Apply remaining operations

222

final = Levenshtein.apply_edit(remaining_ops, intermediate, dest)

223

print(f"Final result: '{final}'") # Should equal dest

224

```

225

226

### Inverse Edit Operations

227

228

Invert the sense of edit operations, swapping source and destination to reverse the transformation direction.

229

230

```python { .api }

231

def inverse(edit_operations):

232

"""

233

Invert the sense of edit operations.

234

235

Returns operations that transform the destination string to the source string.

236

Works with both editops and opcodes.

237

238

Parameters:

239

- edit_operations: List of edit operations to invert

240

241

Returns:

242

list: Inverted edit operations

243

"""

244

```

245

246

**Usage Examples:**

247

248

```python

249

import Levenshtein

250

251

# Get edit operations

252

forward_ops = Levenshtein.editops("spam", "park")

253

print("Forward:", forward_ops)

254

# [('delete', 0, 0), ('insert', 3, 2), ('replace', 3, 3)]

255

256

# Invert operations

257

reverse_ops = Levenshtein.inverse(forward_ops)

258

print("Reverse:", reverse_ops)

259

# [('insert', 0, 0), ('delete', 2, 3), ('replace', 3, 3)]

260

261

# Verify inversion works

262

result = Levenshtein.apply_edit(reverse_ops, "park", "spam")

263

print(f"Reverse transform: 'park' -> '{result}'") # 'park' -> 'spam'

264

265

# Works with opcodes too

266

forward_opcodes = Levenshtein.opcodes("spam", "park")

267

reverse_opcodes = Levenshtein.inverse(forward_opcodes)

268

print("Reversed opcodes:", reverse_opcodes)

269

```

270

271

## Advanced Usage Patterns

272

273

### Analyzing String Transformations

274

275

```python

276

import Levenshtein

277

278

def analyze_transformation(source, dest):

279

"""Detailed analysis of string transformation."""

280

# Get different operation formats

281

editops_list = Levenshtein.editops(source, dest)

282

opcodes_list = Levenshtein.opcodes(source, dest)

283

blocks = Levenshtein.matching_blocks(editops_list, source, dest)

284

285

print(f"Transform '{source}' -> '{dest}'")

286

print(f"Edit operations: {editops_list}")

287

print(f"Opcodes: {opcodes_list}")

288

print(f"Matching blocks: {blocks}")

289

290

# Count operation types

291

op_counts = {}

292

for op, _, _ in editops_list:

293

op_counts[op] = op_counts.get(op, 0) + 1

294

print(f"Operation counts: {op_counts}")

295

296

return editops_list, opcodes_list, blocks

297

298

# Example

299

analyze_transformation("kitten", "sitting")

300

```

301

302

### Step-by-Step Transformation

303

304

```python

305

import Levenshtein

306

307

def step_by_step_transform(source, dest):

308

"""Show each step of the transformation."""

309

ops = Levenshtein.editops(source, dest)

310

current = source

311

312

print(f"Start: '{current}'")

313

314

for i, (op, src_pos, dest_pos) in enumerate(ops, 1):

315

# Apply just this operation

316

single_op = [ops[i-1]] # Current operation

317

# For step-by-step, we need to build operations cumulatively

318

cumulative_ops = ops[:i]

319

current = Levenshtein.apply_edit(cumulative_ops, source, dest)

320

print(f"Step {i} ({op}): '{current}'")

321

322

print(f"Final: '{current}'")

323

324

# Example

325

step_by_step_transform("cat", "dog")

326

```

327

328

### Operation Format Conversion

329

330

```python

331

import Levenshtein

332

333

def convert_operations(source, dest):

334

"""Convert between different operation formats."""

335

# Start with editops

336

editops_list = Levenshtein.editops(source, dest)

337

print("Editops:", editops_list)

338

339

# Convert to opcodes

340

opcodes_list = Levenshtein.opcodes(editops_list, len(source), len(dest))

341

print("Opcodes:", opcodes_list)

342

343

# Convert back to editops

344

editops_converted = Levenshtein.editops(opcodes_list, len(source), len(dest))

345

print("Converted back:", editops_converted)

346

347

# Verify they're equivalent

348

print("Equivalent:", editops_list == editops_converted)

349

350

# Example

351

convert_operations("hello", "world")

352

```

353

354

### Partial Transformations

355

356

```python

357

import Levenshtein

358

359

def partial_transformation_demo(source, dest, steps=None):

360

"""Demonstrate partial application of transformations."""

361

ops = Levenshtein.editops(source, dest)

362

363

if steps is None:

364

steps = len(ops) // 2 # Apply half the operations

365

366

# Apply partial operations

367

partial_ops = ops[:steps]

368

intermediate = Levenshtein.apply_edit(partial_ops, source, dest)

369

370

# Get remaining operations

371

remaining_ops = Levenshtein.subtract_edit(ops, partial_ops)

372

373

# Apply remaining operations

374

final = Levenshtein.apply_edit(remaining_ops, intermediate, dest)

375

376

print(f"Source: '{source}'")

377

print(f"After {steps} operations: '{intermediate}'")

378

print(f"Final: '{final}'")

379

print(f"Matches destination: {final == dest}")

380

381

# Example

382

partial_transformation_demo("programming", "algorithm", 3)

383

```