ソースを参照

"Did you mean" functionality is ready, but needs to be integrated everywhere

default_compile_flags
vector-of-bool 4年前
コミット
931af4895a
5個のファイルの変更136行の追加3行の削除
  1. +7
    -3
      src/dds.main.cpp
  2. +7
    -0
      src/dds/catalog/catalog.cpp
  3. +45
    -0
      src/dds/dym.cpp
  4. +61
    -0
      src/dds/dym.hpp
  5. +16
    -0
      src/dds/dym.test.cpp

+ 7
- 3
src/dds.main.cpp ファイルの表示

@@ -1,6 +1,7 @@
#include <dds/build/builder.hpp>
#include <dds/catalog/catalog.hpp>
#include <dds/catalog/get.hpp>
#include <dds/dym.hpp>
#include <dds/error/errors.hpp>
#include <dds/repo/repo.hpp>
#include <dds/source/dist.hpp>
@@ -197,11 +198,14 @@ struct cli_catalog {
int run() {
auto cat = cat_path.open();
for (const auto& req : requirements.Get()) {
auto id = dds::package_id::parse(req);
auto info = cat.get(id);
auto id = dds::package_id::parse(req);
dds::dym_target dym;
auto info = cat.get(id);
if (!info) {
dds::throw_user_error<dds::errc::no_such_catalog_package>(
"No package in the catalog matched the ID '{}'", req);
"No package in the catalog matched the ID '{}'.{}",
req,
dym.sentence_suffix());
}
auto tsd = dds::get_package_sdist(*info);
auto out_path = out.Get();

+ 7
- 0
src/dds/catalog/catalog.cpp ファイルの表示

@@ -1,5 +1,6 @@
#include "./catalog.hpp"

#include <dds/dym.hpp>
#include <dds/error/errors.hpp>
#include <dds/solve/solve.hpp>

@@ -211,6 +212,12 @@ std::optional<package_info> catalog::get(const package_id& pk_id) const noexcept
std::optional<std::string>,
std::string>(st);
if (!opt_tup) {
dym_target::fill([&] {
auto all_ids = this->all();
auto id_strings
= ranges::views::transform(all_ids, [&](auto id) { return id.to_string(); });
return did_you_mean(pk_id.to_string(), id_strings);
});
return std::nullopt;
}
const auto& [pkg_id, name, version, git_url, git_ref, lm_name, lm_namespace, description]

+ 45
- 0
src/dds/dym.cpp ファイルの表示

@@ -0,0 +1,45 @@
#include <dds/dym.hpp>

#include <range/v3/algorithm/min_element.hpp>
#include <range/v3/view/cartesian_product.hpp>
#include <range/v3/view/iota.hpp>

#include <cassert>

using namespace dds;

thread_local dym_target* dym_target::_tls_current = nullptr;

std::size_t dds::lev_edit_distance(std::string_view a, std::string_view b) noexcept {
const auto n_rows = b.size() + 1;
const auto n_columns = a.size() + 1;

const auto empty_row = std::vector<std::size_t>(n_columns, 0);

std::vector<std::vector<std::size_t>> matrix(n_rows, empty_row);

auto row_iter = ranges::views::iota(1u, n_rows);
auto col_iter = ranges::views::iota(1u, n_columns);

for (auto n : col_iter) {
matrix[0][n] = n;
}
for (auto n : row_iter) {
matrix[n][0] = n;
}

auto prod = ranges::views::cartesian_product(row_iter, col_iter);

for (auto [row, col] : prod) {
auto cost = a[col - 1] == b[row - 1] ? 0 : 1;

auto t1 = matrix[row - 1][col] + 1;
auto t2 = matrix[row][col - 1] + 1;
auto t3 = matrix[row - 1][col - 1] + cost;
auto arr = std::array{t1, t2, t3};

matrix[row][col] = *ranges::min_element(arr);
}

return matrix.back().back();
}

+ 61
- 0
src/dds/dym.hpp ファイルの表示

@@ -0,0 +1,61 @@
#pragma once

#include <range/v3/algorithm/min_element.hpp>
#include <range/v3/view/all.hpp>

#include <optional>
#include <string>
#include <string_view>

namespace dds {

std::size_t lev_edit_distance(std::string_view a, std::string_view b) noexcept;

class dym_target {
std::optional<std::string> _candidate;
dym_target* _tls_prev = nullptr;
static thread_local dym_target* _tls_current;

public:
dym_target()
: _tls_prev(_tls_current) {
_tls_current = this;
}
dym_target(const dym_target&) = delete;
~dym_target() { _tls_current = _tls_prev; }

template <typename Func>
static void fill(Func&& fn) noexcept {
if (_tls_current) {
_tls_current->_candidate = fn();
}
}

auto& candidate() const noexcept { return _candidate; }

std::string sentence_suffix() const noexcept {
if (_candidate) {
return " (Did you mean '" + *_candidate + "'?)";
}
return "";
}
};

template <typename Range>
std::optional<std::string> did_you_mean(std::string_view given, Range&& strings) noexcept {
auto cand = ranges::min_element(strings, ranges::less{}, [&](std::string_view candidate) {
return lev_edit_distance(candidate, given);
});
if (cand == ranges::end(strings)) {
return std::nullopt;
} else {
return std::string(*cand);
}
}

inline std::optional<std::string>
did_you_mean(std::string_view given, std::initializer_list<std::string_view> strings) noexcept {
return did_you_mean(given, ranges::views::all(strings));
}

} // namespace dds

+ 16
- 0
src/dds/dym.test.cpp ファイルの表示

@@ -0,0 +1,16 @@
#include "./dym.hpp"

#include <catch2/catch.hpp>

TEST_CASE("Basic string distance") {
CHECK(dds::lev_edit_distance("a", "a") == 0);
CHECK(dds::lev_edit_distance("a", "b") == 1);
CHECK(dds::lev_edit_distance("aa", "a") == 1);
}

TEST_CASE("Find the 'did-you-mean' candidate") {
auto cand = dds::did_you_mean("food", {"foo", "bar"});
CHECK(cand == "foo");
cand = dds::did_you_mean("eatable", {"edible", "tangible"});
CHECK(cand == "edible");
}

読み込み中…
キャンセル
保存