【问题标题】:How to generate all possible words from atomic word shapes?如何从原子单词形状生成所有可能的单词?
【发布时间】:2021-09-16 01:43:25
【问题描述】:

我正在处理这段代码:

const vowels = ['i', 'a', 'u', 'e', 'E', 'U', 'I', 'o', 'A', 'O', 'o#', 'u#', 'e#', 'i#', 'a#']
const consonants = ['m', 'n', 'q', 'g', 'd', 'b', 'p', 't', 'k', 'h', 'l', 'w', 'f', 's', 'C', 'z', 'v', 'y', 'x', 'r', 'c', 'j', 'Q', 'S', 'Z', '\'']
const tones = ['', '+', '-']
const shapes = [
  '{c}{c}{v}{t1}{v}{t2}',
  '{c}{v}{t1}{v}{t2}',
  '{c}{v}{t}',
  '{c}{c}{v}{t}',
  '{c}!{v}{t}',
]

const words = {}

shapes.forEach(shape => {
  words[shape] = generate(shape)
})

function generate(shape) {
  const sets = {
    v: vowels,
    c: consonants,
    t: tones
  }

  const selectors = {}
  const nodes = []
  shape.replace(/{(\w)(\d)?}/g, (_, $1, $2 = '') => {
    selectors[`${$1}${$2}`] = sets[$1]
    nodes.push(`${$1}${$2}`)
    return _
  })

  // getting lost here
  nodes.forEach(node => {
    
  })
}

function randomBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

console.log(words)

shapes 数组有如下内容:{c}{c}{v}{t1}{v}{t2},其中c 表示辅音,v 表示元音,t 表示音调(来自上述 3 组)。类型旁边的数字只是使我们有不相关的值,而像 {c}{c} 这样的相同键表示相同的辅音两次,而 {c1}{c2} 表示两个不同的辅音。

目标是生成shape 的所有可能组合。我们该怎么做?

对于这个形状{c}{v}{t},我们会开始看到这个结果:

mi
me
ma
mo
mu
...
mi+
me+
ma+
...
ni
ne
na
no
nu
...

我已经尝试过的是:

function cartesian() {
  var arr = [].slice.call(arguments),
      intLength = arr.length,
      arrHelper = [1],
      arrToReturn = [];

  for (var i = arr.length - 1; i >= 0; i--) {
      arrHelper.unshift(arrHelper[0] * arr[i].length);
  }

  for (var i = 0, l = arrHelper[0]; i < l; i++) {
      arrToReturn.push([]);
      for (var j = 0; j < intLength; j++) {
          arrToReturn[i].push(arr[j][(i / arrHelper[j + 1] | 0) % arr[j].length]);
      }
  }

  return arrToReturn;
}

const vowels = [
  'i',
  'a',
  'u',
  'e',
  'E',
  'U',
  'I',
  'o',
  'A',
  'O',
  'o#',
  'u#',
  'e#',
  'i#',
  'a#',
]

const consonants = [
  'm',
  'n',
  'q',
  'g',
  'd',
  'b',
  'p',
  't',
  'k',
  'h',
  'l',
  'w',
  'f',
  's',
  'C',
  'z',
  'v',
  'y',
  'x',
  'r',
  'c',
  'j',
  'Q',
  'S',
  'Z',
  '\'',
]

const tones = [
  '',
  '+',
  '-',
]

const nasals = [
  '',
  '~',
]

const pharyngeals = [
  '',
  '~',
]

const ejectives = [
  '',
  '!',
]

const implosives = [
  '',
  '?',
]

const shapes = [
  '{c}{c}{v}{t1}{v}{t2}',
  '{c}{v}{t1}{v}{t2}',
  '{c}{v}{t}',
  '{c}{c}{v}{t}',
  '{c}!{v}{t}',
]

const words = {}

shapes.forEach(shape => {
  words[shape] = generate(shape)
})

function generate(shape) {
  const sets = {
    v: vowels,
    c: consonants,
    t: tones,
    n: nasals,
    p: pharyngeals
  }

  const selectors = {}
  const string = []
  shape.replace(/{(\w)(\d)?}/g, (_, $1, $2 = '') => {
    selectors[`${$1}${$2}`] = sets[$1]
    string.push(`${$1}${$2}`)
    return _
  })

  const keys = Object.keys(selectors)
  const values = keys.map(selector => selectors[selector])
  const combinations = cartesian(...values)
    .map(nodes => {
      const map = {}
      nodes.forEach((node, i) => map[keys[i]] = node)
      return map
    })

  const result = []
  combinations.forEach(combination => {
    const out = shape.replace(/{(\w+)}/g, (_, $1) => {
      return combination[$1]
    })
    result.push(out)
  })

  return result
}

function randomBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

这里是生成随机值的版本,内存中存储的值太多了。

const vowels = [
  'i',
  'a',
  'u',
  'e',
  'E',
  'U',
  'I',
  'o',
  'A',
  'O',
  'o#',
  'u#',
  'e#',
  'i#',
  'a#',
]

const consonants = [
  'm',
  'n',
  'q',
  'g',
  'd',
  'b',
  'p',
  't',
  'k',
  'h',
  'l',
  'w',
  'f',
  's',
  'C',
  'z',
  'v',
  'y',
  'x',
  'r',
  'c',
  'j',
  'Q',
  'S',
  'Z',
  '\'',
]

const tones = [
  '',
  '+',
  '-',
]

const nasals = [
  '',
  '~',
]

const focusings = [
  '',
  '~',
  '=',
  'Y',
  'w',
  'h',
]

const explosivities = [
  '',
  '!',
  '?',
]

const shapes = [
  '{c}{f}{e}{v}{t}{n}',
  '{c}{f}{e}{v}{t1}{n}{v}{t2}{n}',
  '{c}{f}{e}{c}{f}{e}{v}{t}{n}',
  '{c}{f}{e}{c}{f}{e}{v}{t1}{n}{v}{t2}{n}',
  '{c1}{f1}{e1}{c2}{f2}{e2}{v}{t}{n}',
  '{c}{f}{e}{v1}{t}{n}{v2}{t}{n}',
  '{c}{f}{e}{c}{f}{e}{v1}{t}{n}{v2}{t}{n}',
]

function generate() {
  const sets = {
    v: vowels,
    c: consonants,
    t: tones,
    n: nasals,
    f: focusings,
    e: explosivities
  }

  const shape = shapes[randomBetween(0, shapes.length - 1)]

  const selectors = {}
  const string = []
  shape.replace(/{(\w)(\d)?}/g, (_, $1, $2 = '') => {
    selectors[`${$1}${$2}`] = sets[$1]
    string.push(`${$1}${$2}`)
    return _
  })

  const values = {}
  Object.keys(selectors).forEach(selector => {
    const set = selectors[selector]
    const idx = randomBetween(0, set.length - 1)
    values[selector] = set[idx]
  })

  const result = string.map(selector => values[selector]).join('')

  return result
}

function randomBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

console.log(generate())
console.log(generate())
console.log(generate())
console.log(generate())

【问题讨论】:

  • 试图使其更具可读性
  • !是什么意思?
  • 到目前为止,您尝试了哪些方法来自行解决此问题?它有什么问题?
  • @Andreas 我添加了我的尝试,我猜对了吗?
  • 如果您有工作代码,但正在寻找更简单、更清洁、更高效、更优雅等,那么Code Review 更合适。另外:“这是一个生成随机值的版本”:我不明白为什么会出现在您的问题中,因为您明确 要求 生成 all 可能性。它使问题变得混乱。你是在随机生成之后还是在完全生成之后?

标签: javascript algorithm generator combinations


【解决方案1】:

这不是您问题的确切答案——因此我向 Stack Overflow 纯粹主义者道歉——但它可能对您有所帮助,具体取决于您的目标。所以我想无论如何我都会分享。 :)

Typescript 的template literal types 可以帮助我们。我们首先定义一些字符串联合类型:

type Vowel = 'i' | 'a' | 'u' | 'e' | 'E' | 'U' | 'I' | 'o' | 'A' | 'O' | 'o#' | 'u#' | 'e#' | 'i#' | 'a#'
type Consonant = 'm' | 'n' | 'q' | 'g' | 'd' | 'b' | 'p' | 't' | 'k' | 'h' | 'l' | 'w' | 'f' | 's' | 'C' | 'z' | 'v' | 'y' | 'x' | 'r' | 'c' | 'j' | 'Q' | 'S' | 'Z' | '\''
type Tone = '' | '+' | '-'

这让我们可以使用模板文字类型来为给定的形状/模式创建每个枚举;例如,对于'{c}{v}{t}'

type ShapeString = `${Consonant}${Vowel}${Tone}`

这反过来又允许我们定义一个mapped type,它代表我们的ShapeString的字典:

type EveryCombinationOfShapeString = { [K in ShapeString]: K}

现在您的 IDE 应该能够帮助枚举该类型的对象:

const Dictionary: EveryCombinationOfShapeString = {
  // ...
  "Ce-": 'Ce-',
  "Ci#": 'Ci#',
  "Ci#+": 'Ci#+',
  "Ci#-": 'Ci#-',
  // ...
}

^ 我不会在这里发布整个内容,因为它已经有 1000 多行,只有 3 个字符,但我可以确认 WebStorm 能够在大约 30 秒左右为我实现这个对象的所有成员。

我知道这忽略了您问题的{c1}{c1} 方面,并且还引入了一种新语言,但我希望它仍然会有所帮助。也许您可以在该对象上使用Object.values 并进行一些额外的过滤/映射。

(为清晰起见进行编辑、重命名、文档链接等)

【讨论】:

  • 是的,我承认这一点。但是,它确实会生成一个 js 对象,可以帮助提问者更接近问题的解决方案:)
  • "也许您可以在该对象上使用 Object.values" 整个问题是如何生成所有可能的值。我不认为“手动生成所有排列然后获取它们”是一个非常好的方法。
  • 不是手工制作的。一个称职的 ide 可以实现给定接口的成员;这就是重点
猜你喜欢
  • 1970-01-01
  • 2022-08-24
  • 2018-01-05
  • 1970-01-01
  • 2012-03-03
  • 2017-09-09
  • 1970-01-01
  • 2021-07-26
  • 2013-07-15
相关资源
最近更新 更多