Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 04.01.25 14:02
Оценка: :)
Помните собирали такие задания. Но то для серьезных вещей, а у меня более мелкое, близкое нам — кодинг.

Придумать не получалось — а вот когда затребовала среда — оно как бы само нашлось. Просто взял из рабочего проекта задание, которое можно выделить и реализовать без какой-либо связи с проектом.

Почему это не тривиально? Т.к. либо слишком простое придумывается (на что есть готовый ответ в сети), либо слишком сложное, для которого нужно погружать в детали проекта. А тут как бы вещь в себе, но при этом ответа в сети готового не найдете. Код компилируется, не требует зависимостей от библиотек.

Итак, полный текст, который можно скопировать и дать GPT:

  Для GPT

Язык Dart. Требуется реализовать методы retrieveItems и getItemFromCache менеджера кеширования — класс ObjectDdCacheManager.

Условия:

1. Учитывать FetchDataPolicy — если never, то запрос к API не выполняем, возвращаем то что есть в кеше (или ничего). Если onEmptyCache — то делаем запрос к API только в случае, если в кеше нет данных. Если always — то всегда делаем запрос к API и обновляем данные кеша.

2. Учитывать CacheUpdateMode. Если entities — то только заменяем сущности. Если box — то удаляем старую коробку целиком со всеми объектами.

3. Учитывать фильтр Filter. Если установлен, то запрос к базе делаем через getBeyKey (вид фильтра entityKey) или select (вид range). Фильтр применяем и для удаления сущностей в режиме entities — удаляем все сущности из коробки, которые соответствуют фильтру (а если фильтр не установлен — то просто заменяем сущности методом putAll).

4. Учитывать флаг useMemoryCache. Если установлен, то кроме базы данных используем дополнительно и кеш в памяти — _memoryCache. С ним работаем так же как с базой (можно добавить обертку по аналогии с моим моком ObjectDdMock). Если данные есть в _memoryCache и не нужно обновлять данные — в базу не лезем.

Для удобства, чтобы код собирался, добавлен раздел с моками — его трогать не нужно.


// ****************************** MOCKS  ******************************

class ErrorInfo {
  final int errorCode;
  ErrorInfo(this.errorCode);
}

class ResultOrError<TResult> {
  late bool isSuccess;
  TResult? result;
  ErrorInfo? error;

  ResultOrError._fromResult(this.result) {
    isSuccess = true;
    error = null;
  }

  ResultOrError._fromError(this.error) {
    isSuccess = false;
    result = null;
  }

  factory ResultOrError.fromResult(TResult result) =>
      ResultOrError._fromResult(result);
  factory ResultOrError.fromError(ErrorInfo error) =>
      ResultOrError._fromError(error);
}

class ApiObject1 {
  final Map<String, String> items = {"item1": "value1", "item2": "value2"};
}

abstract class DataEntity {
  String get key;
}

class DataEntity1 extends DataEntity {
  String name;
  String value;

  @override
  String get key => name;

  DataEntity1(this.name, this.value);
}

class Model1 {
  final String item;
  Model1(this.item);
}

class ObjectDdMock<TDataEntity extends DataEntity> {
  String get boxKey => TDataEntity.runtimeType.toString();

  static final Map<String, Map<String, dynamic>> _boxes = {};

  List<TDataEntity> getAll() {
    return _boxes[boxKey]?.values.cast<TDataEntity>().toList() ?? [];
  }

  TDataEntity? getBeyKey(String key) {
    final box = _boxes[boxKey];

    if (null == box) return null;
    return box[key] as TDataEntity;
  }

  List<TDataEntity> select(String startKey, String endKey) {
    final box = _boxes[boxKey];

    if (null == box) return [];

    final allEntities = getAll();
    List<String> keys = allEntities
        .map((e) => e.key)
        .where((k) => k.compareTo(startKey) >= 0 && k.compareTo(endKey) <= 0)
        .toList();
    keys.sort();

    List<TDataEntity> result = [];

    for (final key in keys) {
      result.add(box[key]!);
    }

    return result;
  }

  void putAll(List<TDataEntity> entities) {
    for (final entity in entities) {
      put(entity);
    }
  }

  void put(TDataEntity entity) {
    if (!_boxes.containsKey(boxKey)) {
      _boxes[boxKey] = {entity.key: entity};
    } else {
      _boxes[boxKey]![entity.key] = entity;
    }
  }

  void deleteBox() {
    if (_boxes.containsKey(boxKey)) {
      _boxes.remove(boxKey);
    }
  }

  void deleteEntities(List<TDataEntity> entities) {
    final box = _boxes[boxKey];

    if (null != box) {
      for (final entity in entities) {
        box.remove(entity.key);
      }
    }
  }
}

// ****************************** END MOCKS  ******************************

enum FetchDataPolicy { never, onEmptyCache, always }

enum CacheUpdateMode { entities, box }

enum FilterKind { entityKey, range }

abstract class Filter {
  FilterKind get kind;
}

class FilterByKey implements Filter {
  String key;

  @override
  FilterKind get kind => FilterKind.entityKey;

  FilterByKey(this.key);
}

class FilterByRange implements Filter {
  String startKey;
  String endKey;

  @override
  FilterKind get kind => FilterKind.range;

  FilterByRange({required this.startKey, required this.endKey});
}

abstract class CacheManager<TApiObject, TDataEntity extends DataEntity,
    TModel> {
  final Future<ResultOrError<TApiObject>> Function() fetchFromApi;
  final List<TDataEntity> Function(TApiObject) transformToDataEntities;
  final TModel Function(TDataEntity) transformToModel;

  final String boxName;
  final bool useMemoryCache;

  CacheManager(
      {required this.fetchFromApi,
      required this.transformToDataEntities,
      required this.transformToModel,
      required this.boxName,
      this.useMemoryCache = false});

  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter});

  Future<TModel?> getItemFromCache(String key);
}

final Map<String, Map<String, dynamic>> _memoryCache = {};

class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName});

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) {
    // TODO: implement retrieveItems
    throw UnimplementedError();
  }

  @override
  Future<TModel?> getItemFromCache(String key) {
    // TODO: implement getItemFromCache
    throw UnimplementedError();
  }
}

void main() async {
  final cacheManager = ObjectDdCacheManager(
      fetchFromApi: () async {
        return ResultOrError.fromResult(ApiObject1());
      },
      transformToDataEntities: (apiObject) {
        return apiObject.items.keys
            .map((k) => DataEntity1(k, apiObject.items[k]!))
            .toList();
      },
      transformToModel: (dataEntity) {
        return Model1(dataEntity.value);
      },
      boxName: 'box1');

  final items = await cacheManager.retrieveItems();

  print(items.result!.length);
}



Код с возможностью запуска тут: http://tpcg.io/_WD2ZDE

Вот такой результат выдает

  GPT-4o
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager({
    required super.fetchFromApi,
    required super.transformToDataEntities,
    required super.transformToModel,
    required super.boxName,
  });

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems({
    FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
    CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
    Filter? filter,
  }) async {
    final memoryCache = _memoryCache[boxName];
    final ObjectDdMock<TDataEntity> dbCache = ObjectDdMock<TDataEntity>();
    List<TDataEntity> entities;

    // Работа с кешем в памяти
    if (useMemoryCache && memoryCache != null) {
      entities = _filterEntities(memoryCache.values.cast<TDataEntity>(), filter);
      if (fetchDataPolicy == FetchDataPolicy.never || (fetchDataPolicy == FetchDataPolicy.onEmptyCache && entities.isNotEmpty)) {
        return ResultOrError.fromResult(entities.map(transformToModel).toList());
      }
    }

    // Работа с кешем в базе
    entities = _filterEntities(dbCache.getAll(), filter);
    if (fetchDataPolicy == FetchDataPolicy.never) {
      return ResultOrError.fromResult(entities.map(transformToModel).toList());
    }

    if (fetchDataPolicy == FetchDataPolicy.onEmptyCache && entities.isNotEmpty) {
      return ResultOrError.fromResult(entities.map(transformToModel).toList());
    }

    // Запрос данных из API
    final apiResult = await fetchFromApi();
    if (!apiResult.isSuccess) {
      return ResultOrError.fromError(apiResult.error!);
    }

    final newEntities = transformToDataEntities(apiResult.result!);
    if (cacheUpdateMode == CacheUpdateMode.entities) {
      if (filter != null) {
        final filteredEntities = _filterEntities(dbCache.getAll(), filter);
        dbCache.deleteEntities(filteredEntities);
      }
      dbCache.putAll(newEntities);
    } else if (cacheUpdateMode == CacheUpdateMode.box) {
      dbCache.deleteBox();
      dbCache.putAll(newEntities);
    }

    if (useMemoryCache) {
      _memoryCache[boxName] = {for (var e in newEntities) e.key: e};
    }

    return ResultOrError.fromResult(newEntities.map(transformToModel).toList());
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    if (useMemoryCache) {
      final memoryCache = _memoryCache[boxName];
      if (memoryCache != null && memoryCache.containsKey(key)) {
        return transformToModel(memoryCache[key] as TDataEntity);
      }
    }

    final ObjectDdMock<TDataEntity> dbCache = ObjectDdMock<TDataEntity>();
    final entity = dbCache.getBeyKey(key);
    if (entity != null) {
      if (useMemoryCache) {
        _memoryCache[boxName] ??= {};
        _memoryCache[boxName]![key] = entity;
      }
      return transformToModel(entity);
    }

    return null;
  }

  List<TDataEntity> _filterEntities(List<TDataEntity> entities, Filter? filter) {
    if (filter == null) return entities;

    if (filter.kind == FilterKind.entityKey) {
      final key = (filter as FilterByKey).key;
      return entities.where((e) => e.key == key).toList();
    } else if (filter.kind == FilterKind.range) {
      final startKey = (filter as FilterByRange).startKey;
      final endKey = filter.endKey;
      return entities.where((e) => e.key.compareTo(startKey) >= 0 && e.key.compareTo(endKey) <= 0).toList();
    }

    return entities;
  }
}


Тут: https://chatgpt.com/share/67793e85-4a44-8000-b3e3-fa47a8d2663f

Вот o1-preview через API — ответ стоил $0.14

  o1-preview
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity,
    TModel> extends CacheManager<TApiObject, TDataEntity, TModel> {
  // Define the boxKey using TDataEntity's type
  final String boxKey = TDataEntity.toString();

  // Create an instance of ObjectDdMock for database operations
  final ObjectDdMock<TDataEntity> _database = ObjectDdMock<TDataEntity>();

  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName,
      super.useMemoryCache = false});

  // Helper method to get data from cache (memory or database)
  Future<List<TDataEntity>> _getDataFromCache(Filter? filter) async {
    if (useMemoryCache) {
      // Try to get data from _memoryCache
      final box = _memoryCache[boxKey];
      if (box != null) {
        if (filter == null) {
          // Return all data
          return box.values.cast<TDataEntity>().toList();
        } else if (filter.kind == FilterKind.entityKey) {
          FilterByKey filterByKey = filter as FilterByKey;
          final entity = box[filterByKey.key] as TDataEntity?;
          return entity != null ? [entity] : [];
        } else if (filter.kind == FilterKind.range) {
          FilterByRange filterByRange = filter as FilterByRange;
          final entities = box.values
              .cast<TDataEntity>()
              .where((e) =>
                  e.key.compareTo(filterByRange.startKey) >= 0 &&
                  e.key.compareTo(filterByRange.endKey) <= 0)
              .toList();
          return entities;
        }
      }
    }
    // Get data from database
    if (filter == null) {
      return _database.getAll();
    } else if (filter.kind == FilterKind.entityKey) {
      FilterByKey filterByKey = filter as FilterByKey;
      final entity = _database.getBeyKey(filterByKey.key);
      return entity != null ? [entity] : [];
    } else if (filter.kind == FilterKind.range) {
      FilterByRange filterByRange = filter as FilterByRange;
      return _database.select(filterByRange.startKey, filterByRange.endKey);
    }
    return [];
  }

  // Helper method to store data into cache (memory and database)
  Future<void> _storeDataInCache(List<TDataEntity> entities,
      CacheUpdateMode cacheUpdateMode, Filter? filter) async {
    if (cacheUpdateMode == CacheUpdateMode.box) {
      if (useMemoryCache) {
        // Remove the box from _memoryCache and add entities
        _memoryCache[boxKey] = {for (var e in entities) e.key: e};
      }
      // Delete box from database and add entities
      _database.deleteBox();
      _database.putAll(entities);
    } else if (cacheUpdateMode == CacheUpdateMode.entities) {
      if (filter != null) {
        // Delete entities matching the filter
        if (useMemoryCache) {
          final box = _memoryCache[boxKey];
          if (box != null) {
            if (filter.kind == FilterKind.entityKey) {
              FilterByKey filterByKey = filter as FilterByKey;
              box.remove(filterByKey.key);
            } else if (filter.kind == FilterKind.range) {
              FilterByRange filterByRange = filter as FilterByRange;
              final keysToRemove = box.keys
                  .where((k) =>
                      k.compareTo(filterByRange.startKey) >= 0 &&
                      k.compareTo(filterByRange.endKey) <= 0)
                  .toList();
              for (final k in keysToRemove) {
                box.remove(k);
              }
            }
          }
        }
        // Delete entities from the database
        if (filter.kind == FilterKind.entityKey) {
          FilterByKey filterByKey = filter as FilterByKey;
          final entity = _database.getBeyKey(filterByKey.key);
          if (entity != null) {
            _database.deleteEntities([entity]);
          }
        } else if (filter.kind == FilterKind.range) {
          FilterByRange filterByRange = filter as FilterByRange;
          final entitiesToDelete =
              _database.select(filterByRange.startKey, filterByRange.endKey);
          _database.deleteEntities(entitiesToDelete);
        }
      }
      // Add new entities
      if (useMemoryCache) {
        final box = _memoryCache.putIfAbsent(boxKey, () => {});
        for (var e in entities) {
          box[e.key] = e;
        }
      }
      _database.putAll(entities);
    }
  }

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) async {
    try {
      bool needToFetch = false;
      List<TDataEntity> cachedEntities = await _getDataFromCache(filter);

      if (fetchDataPolicy == FetchDataPolicy.always) {
        needToFetch = true;
      } else if (fetchDataPolicy == FetchDataPolicy.onEmptyCache &&
          cachedEntities.isEmpty) {
        needToFetch = true;
      } else if (fetchDataPolicy == FetchDataPolicy.never) {
        needToFetch = false;
      }

      if (needToFetch) {
        // Fetch from API
        final result = await fetchFromApi();
        if (!result.isSuccess) {
          return ResultOrError.fromError(result.error!);
        }
        // Transform API object to data entities
        List<TDataEntity> entities =
            transformToDataEntities(result.result as TApiObject);
        // Update cache
        await _storeDataInCache(entities, cacheUpdateMode, filter);
        // Update cachedEntities with newly fetched data
        cachedEntities = await _getDataFromCache(filter);
      }

      // Transform data entities to models
      final models = cachedEntities.map(transformToModel).toList();
      return ResultOrError.fromResult(models);
    } catch (e) {
      return ResultOrError.fromError(ErrorInfo(-1)); // Replace with appropriate error code
    }
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    try {
      if (useMemoryCache) {
        final box = _memoryCache[boxKey];
        if (box != null) {
          final entity = box[key] as TDataEntity?;
          if (entity != null) {
            return transformToModel(entity);
          }
        }
      }
      // Get from database
      final entity = _database.getBeyKey(key);
      if (entity != null) {
        return transformToModel(entity);
      }
      return null;
    } catch (e) {
      return null;
    }
  }
}


  Google Gemini
final Map<String, Map<String, dynamic>> _memoryCache = {};

class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName,
      super.useMemoryCache});

  ObjectDdMock<TDataEntity> get _db => ObjectDdMock<TDataEntity>();

    ObjectDdMock<TDataEntity> get _memory => ObjectDdMock<TDataEntity>();


  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) async {
    List<TDataEntity> cachedEntities = [];

    if (useMemoryCache) {
      cachedEntities = _memory.getAll();
    }

    if (cachedEntities.isEmpty) {
      cachedEntities = _db.getAll();
    }

    if (fetchDataPolicy == FetchDataPolicy.never) {
      return ResultOrError.fromResult(
          cachedEntities.map(transformToModel).toList());
    }

    if (fetchDataPolicy == FetchDataPolicy.onEmptyCache &&
        cachedEntities.isNotEmpty) {
      return ResultOrError.fromResult(
          cachedEntities.map(transformToModel).toList());
    }

    final apiResult = await fetchFromApi();
    if (!apiResult.isSuccess) {
      return ResultOrError.fromError(apiResult.error!);
    }

    final newEntities = transformToDataEntities(apiResult.result!);

    if (cacheUpdateMode == CacheUpdateMode.box) {
      _db.deleteBox();
      if (useMemoryCache) {
        _memory.deleteBox();
      }
    } else if (filter != null) {
      List<TDataEntity> entitiesToDelete = [];
      if (filter.kind == FilterKind.entityKey) {
        final keyFilter = filter as FilterByKey;
        final entity = _db.getBeyKey(keyFilter.key);
        if (entity != null) {
          entitiesToDelete.add(entity);
        }
          if (useMemoryCache) {
            final memoryEntity = _memory.getBeyKey(keyFilter.key);
            if (memoryEntity != null) {
              entitiesToDelete.add(memoryEntity);
            }
          }
      } else if (filter.kind == FilterKind.range) {
        final rangeFilter = filter as FilterByRange;
        entitiesToDelete = _db.select(rangeFilter.startKey, rangeFilter.endKey);
        if (useMemoryCache) {
          entitiesToDelete.addAll(_memory.select(rangeFilter.startKey, rangeFilter.endKey));
        }
      }
      _db.deleteEntities(entitiesToDelete);
      if (useMemoryCache) {
        _memory.deleteEntities(entitiesToDelete);
      }
    }

    _db.putAll(newEntities);
        if (useMemoryCache) {
      _memory.putAll(newEntities);
    }

    return ResultOrError.fromResult(newEntities.map(transformToModel).toList());
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    if (useMemoryCache) {
      final memoryEntity = _memory.getBeyKey(key);
      if (memoryEntity != null) {
        return transformToModel(memoryEntity);
      }
    }
    final entity = _db.getBeyKey(key);
    return entity != null ? transformToModel(entity) : null;
  }
}


Так же просьба, у кого есть возможность, позапускать на других версиях. Позже добавлю комменты.

З.Ы.
Так же добавляйте свое понимание — хорошее ли задание для ИИ, сколько бы у вас заняло времени.
Отредактировано 04.01.2025 22:17 Shmj . Предыдущая версия . Еще …
Отредактировано 04.01.2025 15:03 Shmj . Предыдущая версия .
Отредактировано 04.01.2025 14:03 Shmj . Предыдущая версия .
Re: Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 07.01.25 10:07
Оценка: :)
Здравствуйте, Shmj, Вы писали:

S>Позже добавлю комменты.


Странно что всем пофиг на тему. Видимо причина — задание не тривиальное и просто лень вникать и читать код за бесплатно.

Выводы мои такие. Вроде o1-preview и Gemini дали рабочий код (хотя в проект я такое добавить не могу и сильно не вчитывался). Gemini даже как будто не просто прошлась по условиям, но и проявила признаки осмысленности.

Конечно, он говнокодистый получился — но ведь такой код не нужно поддерживать — его просто генерят заново по новому запросу.

Основная проблема — пока нет доверия что код нормальный, а разбирать его долго, ввиду плохого качества кода.
Re: Нашел задание для проверки возможностей ИИ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 07.01.25 12:38
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Помните собирали такие задания.


Попросите ИИ пересказать положения закона о какой-либо определенной ситуации, затем соотнесите то, что он выдаст под видом цитат, с текстом соответствующих статей.
Re[2]: Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 08.01.25 17:09
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Попросите ИИ пересказать положения закона о какой-либо определенной ситуации, затем соотнесите то, что он выдаст под видом цитат, с текстом соответствующих статей.


Мне интереснее что-то связанное с кодом.

Вот, кстати, начал проверять Google Gemini. Он вроде лучше пишет код без зависимостей от сторонних библиотек, т.к. тупо в них ничего не шарит. А GPT не такой умный, зато все библиотеки досконально знает.
Re[3]: Нашел задание для проверки возможностей ИИ
От: rudzuk  
Дата: 08.01.25 18:41
Оценка:
Здравствуйте, Shmj, Вы писали:

S> Вот, кстати, начал проверять Google Gemini. Он вроде лучше пишет код без зависимостей от сторонних библиотек, т.к. тупо в них ничего не шарит. А GPT не такой умный, зато все библиотеки досконально знает.


Хрен он чего знает. Предагал использовать gzread из zlib для чтения заголовка архива После таких предложений ему самое место в стене, о которую он убьется.
avalon/3.0.2
Re[3]: Нашел задание для проверки возможностей ИИ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 08.01.25 19:53
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Мне интереснее что-то связанное с кодом.


Ну да, кодом фантазировать сложнее.
Re[2]: Нашел задание для проверки возможностей ИИ
От: vdimas Россия  
Дата: 09.01.25 13:51
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Конечно, он говнокодистый получился — но ведь такой код не нужно поддерживать — его просто генерят заново по новому запросу.


Экспериментировал с Cursor AI.
По запросу генерит просто говнокод.

А когда начинаешь уточнять, через что именно реализовать то-то и то-то, в т.ч. через хелперы в этом же проекте — генерит уже вполне.
Re: Нашел задание для проверки возможностей ИИ
От: vsb Казахстан  
Дата: 13.01.25 08:27
Оценка: 8 (1)
  ChatGPT o1 pro
Below is one possible reference implementation for the ObjectDdCacheManager class. The key points are:

Respecting the FetchDataPolicy:
never: do not call the API at all.
onEmptyCache: call the API only if the cache (memory or DB) is empty.
always: always call the API.
Respecting the CacheUpdateMode:
entities: delete only the filtered entities (if a filter is provided). Otherwise, just putAll the new ones and do not remove older items that are outside the new batch.
box: delete the entire box, then store the new items.
Respecting Filter:
If FilterByKey, retrieve using getBeyKey.
If FilterByRange, retrieve using select.
If no filter, use getAll.
In entities mode, if filter is set, delete only the matching entities; if no filter is set, simply do putAll.
Respecting useMemoryCache:
If true, we use both _memoryCache and the database.
If data already exists in _memoryCache (and doesn’t need updating), do not hit the DB.
Whenever we write or delete, we do it in both _memoryCache and DB.
getItemFromCache(key): purely local lookup; do not call the API.

class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager({
    required super.fetchFromApi,
    required super.transformToDataEntities,
    required super.transformToModel,
    required super.boxName,
    super.useMemoryCache = false,
  });

  // We'll use this DB mock inside our methods.
  final ObjectDdMock<TDataEntity> _db = ObjectDdMock<TDataEntity>();

  /// Helper methods for in-memory cache lookups and mutations
  Map<String, TDataEntity>? get _memoryBox {
    return _memoryCache[boxName]?.cast<String, TDataEntity>();
  }

  List<TDataEntity> _memoryGetAll() {
    if (_memoryBox == null) return [];
    return _memoryBox!.values.toList();
  }

  TDataEntity? _memoryGetByKey(String key) {
    return _memoryBox == null ? null : _memoryBox![key];
  }

  List<TDataEntity> _memorySelect(String startKey, String endKey) {
    final all = _memoryGetAll();
    final result = all
        .where((e) =>
            e.key.compareTo(startKey) >= 0 && e.key.compareTo(endKey) <= 0)
        .toList();
    result.sort((a, b) => a.key.compareTo(b.key));
    return result;
  }

  void _memoryPutAll(List<TDataEntity> entities) {
    final box = _memoryCache.putIfAbsent(boxName, () => <String, TDataEntity>{});
    for (final entity in entities) {
      box[entity.key] = entity;
    }
  }

  void _memoryDeleteBox() {
    _memoryCache.remove(boxName);
  }

  void _memoryDeleteEntities(List<TDataEntity> entities) {
    final box = _memoryBox;
    if (box != null) {
      for (final entity in entities) {
        box.remove(entity.key);
      }
    }
  }

  /// Retrieve items from the cache according to the filter
  List<TDataEntity> _getFromCache(Filter? filter) {
    // 1) If `useMemoryCache` is true and memory has data for that box,
    //    do not access the DB (as per requirement).
    // 2) Otherwise, read from DB.
    List<TDataEntity> result;

    final hasMemory = useMemoryCache && _memoryBox != null && _memoryBox!.isNotEmpty;
    if (hasMemory) {
      switch (filter?.kind) {
        case FilterKind.entityKey:
          final key = (filter as FilterByKey).key;
          final item = _memoryGetByKey(key);
          result = item == null ? [] : [item];
          break;
        case FilterKind.range:
          final range = filter as FilterByRange;
          result = _memorySelect(range.startKey, range.endKey);
          break;
        default:
          // No filter
          result = _memoryGetAll();
      }
      return result;
    }

    // Fallback to DB if memory is not used or empty
    switch (filter?.kind) {
      case FilterKind.entityKey:
        final key = (filter as FilterByKey).key;
        final item = _db.getBeyKey(key);
        result = item == null ? [] : [item];
        break;
      case FilterKind.range:
        final range = filter as FilterByRange;
        result = _db.select(range.startKey, range.endKey);
        break;
      default:
        // No filter
        result = _db.getAll();
    }
    return result;
  }

  /// Store new entities in the cache/DB according to the update mode + filter
  void _storeEntities(
    List<TDataEntity> newEntities,
    CacheUpdateMode cacheUpdateMode,
    Filter? filter,
  ) {
    if (cacheUpdateMode == CacheUpdateMode.box) {
      // Delete the entire box from memory and DB, then put all
      _db.deleteBox();
      if (useMemoryCache) {
        _memoryDeleteBox();
      }
      _db.putAll(newEntities);
      if (useMemoryCache) {
        _memoryPutAll(newEntities);
      }
      return;
    }

    // If we are here, then `cacheUpdateMode == CacheUpdateMode.entities`
    if (filter != null) {
      // Delete only the matching entities from DB & memory
      final oldEntities = _getFromCache(filter);
      _db.deleteEntities(oldEntities);
      if (useMemoryCache) {
        _memoryDeleteEntities(oldEntities);
      }
    }
    // Then put the new ones
    _db.putAll(newEntities);
    if (useMemoryCache) {
      _memoryPutAll(newEntities);
    }
  }

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems({
    FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
    CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
    Filter? filter,
  }) async {
    try {
      // 1) Check if cache is empty (depending on useMemoryCache).
      final cachedData = _getFromCache(filter);
      final bool isCacheEmpty = cachedData.isEmpty;

      // 2) Decide if we need to fetch from API
      bool needApiFetch = false;
      switch (fetchDataPolicy) {
        case FetchDataPolicy.never:
          // never call the API
          needApiFetch = false;
          break;
        case FetchDataPolicy.onEmptyCache:
          // call the API only if the cache is empty
          needApiFetch = isCacheEmpty;
          break;
        case FetchDataPolicy.always:
          // always call the API
          needApiFetch = true;
          break;
      }

      List<TDataEntity> finalEntities = cachedData;

      if (needApiFetch) {
        final fetchResult = await fetchFromApi();
        if (!fetchResult.isSuccess) {
          // Return the error if API call failed
          return ResultOrError.fromError(fetchResult.error!);
        }
        // Transform the API object to data entities
        final newEntities = transformToDataEntities(fetchResult.result!);

        // Write them to DB and/or memory, depending on the update mode
        _storeEntities(newEntities, cacheUpdateMode, filter);

        // Now re-query the cache to get the final list
        finalEntities = _getFromCache(filter);
      }

      // Convert all TDataEntities to TModels
      final models = finalEntities.map(transformToModel).toList();
      return ResultOrError.fromResult(models);
    } catch (e) {
      // In a real system, you'd have better error handling
      return ResultOrError.fromError(ErrorInfo(-1));
    }
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    // 1) If useMemoryCache is true, try memory first
    if (useMemoryCache) {
      final memItem = _memoryGetByKey(key);
      if (memItem != null) {
        return transformToModel(memItem);
      }
    }

    // 2) Fallback to DB
    final dbItem = _db.getBeyKey(key);
    if (dbItem != null) {
      return transformToModel(dbItem);
    }

    // 3) Not found at all
    return null;
  }
}


Explanation of Key Parts
_getFromCache(filter):
Checks in-memory first (if useMemoryCache is set and memory has data).
If memory doesn’t have data (or useMemoryCache is false), queries the database.
Respects the FilterKind — calls either getBeyKey(...), select(...), or getAll().
_storeEntities(newEntities, cacheUpdateMode, filter):
If box mode:
Delete the entire box from both DB and memory, then putAll the new entities.
If entities mode:
If a filter is present, remove only the matching entities from both DB and memory.
putAll the new entities without removing anything else.
This follows the requirement to only replace matching entities in entities mode and fully replace everything in box mode.
retrieveItems handles:
Check if cache is empty: to decide if we need to call the API for onEmptyCache.
Conditionally fetch from API (for onEmptyCache or always); skip for never.
Transform new data to TDataEntity, store them, then read them again and produce List<TModel>.
getItemFromCache(key):
Purely local lookup:
If useMemoryCache, look in _memoryCache.
Otherwise (or if not found in memory), look in DB.
Return null if not found.
With this implementation, you can run the provided main() function and see that items are fetched, stored, and transformed properly. Then you can experiment by changing the policies (FetchDataPolicy), filter, and update modes.


За форматированием по ссылке...

https://chatgpt.com/share/6784ce71-11d4-800a-9aa9-e0ea06a1c909
Re[2]: Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 13.01.25 10:18
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>[cut=ChatGPT o1 pro]

vsb>https://chatgpt.com/share/6784ce71-11d4-800a-9aa9-e0ea06a1c909

Ого! Выглядит разумнее чем Google Gemini.

Можно еще проверить этот пример: https://www.programiz.com/online-compiler/165XEq7dCoO1f — сможет ли написать DeobfuscateString на C#.
Re[3]: Нашел задание для проверки возможностей ИИ
От: vsb Казахстан  
Дата: 13.01.25 12:10
Оценка: 8 (1)
Суровая задача. Выдал такое: https://chatgpt.com/share/67850298-1dd8-800a-a0ec-4aeb386ffa7a (при запуске декодирует мусор, так что не справился)

using System;
using System.Collections.Generic;
using System.Text;

public class Obfuscation
{
    public static List<int> ObfuscateString(string input, List<int> key1, List<int> key2)
    {
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        List<int> result = new List<int>();
        int prevChar = 0;

        for (int i = 0; i < inputBytes.Length; i++)
        {
            int character = inputBytes[i];

            int key1Index = (prevChar + i) % key1.Count;
            int key2Index = (key1[key1Index] + prevChar) % key2.Count;

            // Apply 10 obfuscation steps
            for (int j = 0; j < 10; j++)
            {
                // 1
                character = ((character + key1[key1Index] + prevChar) ^ key2[key2Index]) & 0xFF;
                // 2 (rotate left by 3 bits)
                character = ((character << 3) | (character >> 5)) & 0xFF;
                // 3
                character = (character ^ key1[key1Index]) & 0xFF;
                // 4
                character = ((character - key2[key2Index]) & 0xFF) ^ 0xAB;
                // 5
                character = (character * 7) & 0xFF;
                // 6
                character = (character ^ (key1[key1Index] >> 2)) & 0xFF;
                // 7
                character = ((character + key2[key2Index] + prevChar) & 0xFF) ^ 0x7E;
                // 8 (rotate left by 2 bits)
                character = ((character << 2) | (character >> 6)) & 0xFF;
                // 9
                character = (character ^ 0x55) & 0xFF;
                // 10
                character = ((character - key1[key1Index] + prevChar) * 3) & 0xFF;
            }

            result.Add(character);
            prevChar = character;
        }

        return result;
    }

    public static string DeobfuscateString(List<int> obfuscatedData, List<int> key1, List<int> key2)
    {
        // Modular inverses in the range [0..255]
        // 3^-1 mod 256 = 171
        // 7^-1 mod 256 = 183
        const int INV_3 = 171;
        const int INV_7 = 183;

        List<byte> originalBytes = new List<byte>();
        int prevChar = 0;

        for (int i = 0; i < obfuscatedData.Count; i++)
        {
            // This is the *obfuscated* byte
            int c = obfuscatedData[i];

            // Compute key indices based on the same logic as the forward pass
            int key1Index = (prevChar + i) % key1.Count;
            int key2Index = (key1[key1Index] + prevChar) % key2.Count;

            // Invert the 10 obfuscation steps in reverse order:

            // Step 10 inverse: c = ((x - key1 + prevChar) * 3) & 0xFF
            // => x = (c * INV_3) & 0xFF
            // => x = x + key1 - prevChar
            c = (c * INV_3) & 0xFF;
            c = (c + key1[key1Index] - prevChar) & 0xFF;

            // Step 9 inverse: c = (x ^ 0x55) & 0xFF
            // => x = c ^ 0x55
            c = c ^ 0x55;

            // Step 8 inverse: c = rotateLeft(x, 2)
            // => x = rotateRight(c, 2)
            c = ((c >> 2) | (c << 6)) & 0xFF;

            // Step 7 inverse: c = ((x + key2 + prevChar) ^ 0x7E)
            // => x = (c ^ 0x7E) - (key2 + prevChar)
            c = c ^ 0x7E;
            c = (c - key2[key2Index] - prevChar) & 0xFF;

            // Step 6 inverse: c = x ^ (key1 >> 2)
            c = c ^ (key1[key1Index] >> 2);

            // Step 5 inverse: c = x * 7
            // => x = c * INV_7
            c = (c * INV_7) & 0xFF;

            // Step 4 inverse: c = ((x - key2) & 0xFF) ^ 0xAB
            // => x = (c ^ 0xAB) + key2
            c = c ^ 0xAB;
            c = (c + key2[key2Index]) & 0xFF;

            // Step 3 inverse: c = x ^ key1
            c = c ^ key1[key1Index];

            // Step 2 inverse: c = rotateLeft(x, 3)
            // => x = rotateRight(c, 3)
            c = ((c >> 3) | (c << 5)) & 0xFF;

            // Step 1 inverse: c = ((x + key1 + prevChar) ^ key2) & 0xFF
            // => x = (c ^ key2) - (key1 + prevChar)
            c = c ^ key2[key2Index];
            c = (c - (key1[key1Index] + prevChar)) & 0xFF;

            // This 'c' is now the original (deobfuscated) byte
            originalBytes.Add((byte)c);

            // Important: prevChar in the next iteration is the *obfuscated* byte,
            // just as in the forward pass it was set to the newly-obfuscated character.
            prevChar = obfuscatedData[i];
        }

        return Encoding.UTF8.GetString(originalBytes.ToArray());
    }

    public static void Main()
    {
        string input = "Hello, World!";
        List<int> key1 = new List<int> { 1, 2, 3, 4, 5 };
        List<int> key2 = new List<int> { 6, 7, 8, 9, 10 };

        // Obfuscate
        List<int> obfuscated = ObfuscateString(input, key1, key2);
        Console.WriteLine("Obfuscated:");
        Console.WriteLine(string.Join(", ", obfuscated));

        // Deobfuscate
        string deobfuscated = DeobfuscateString(obfuscated, key1, key2);
        Console.WriteLine("Deobfuscated:");
        Console.WriteLine(deobfuscated);

        // Verify
        Console.WriteLine("Test: " + (input == deobfuscated ? "success!" : "fail"));
    }
}
Отредактировано 13.01.2025 12:11 vsb . Предыдущая версия .
Re[4]: Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 13.01.25 13:30
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Суровая задача. Выдал такое: https://chatgpt.com/share/67850298-1dd8-800a-a0ec-4aeb386ffa7a (при запуске декодирует мусор, так что не справился)


Интересно что o1 — эту задачу решает. Получается все как у людей — иногда много ума даже вредит
Отредактировано 13.01.2025 13:30 Shmj . Предыдущая версия .
Re[2]: Нашел задание для проверки возможностей ИИ
От: student__  
Дата: 13.01.25 14:37
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Странно что всем пофиг на тему. Видимо причина — задание не тривиальное и просто лень вникать и читать код за бесплатно.


Я, например, не знаю этого языка, и оценить нетривиальность кода не могу.
Re: Нашел задание для проверки возможностей ИИ
От: Lepsik Индия figvam.ca
Дата: 16.01.25 07:05
Оценка: 6 (1)
mistral

Для реализации методов retrieveItems и getItemFromCache в классе ObjectDdCacheManager, нам нужно учитывать все условия, описанные в задаче. Давайте начнем с реализации этих методов.
Реализация метода retrieveItems

FetchDataPolicy:
never: Возвращаем данные из кеша (или ничего).
onEmptyCache: Делаем запрос к API только если в кеше нет данных.
always: Всегда делаем запрос к API и обновляем данные кеша.

CacheUpdateMode:
entities: Заменяем сущности.
box: Удаляем старую коробку целиком со всеми объектами.

Filter:
Если установлен, то запрос к базе делаем через getBeyKey (вид фильтра entityKey) или select (вид range).
Фильтр применяем и для удаления сущностей в режиме entities.

useMemoryCache:
Если установлен, то используем дополнительно кеш в памяти _memoryCache.

Реализация метода getItemFromCache

useMemoryCache:
Если установлен, то сначала проверяем кеш в памяти _memoryCache.
Если данных нет в _memoryCache, то проверяем базу данных.

  code
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName});

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) async {
    final box = ObjectDdMock<TDataEntity>();
    List<TDataEntity> cachedEntities = [];

    // Check memory cache first if useMemoryCache is true
    if (useMemoryCache && _memoryCache.containsKey(boxName)) {
      cachedEntities = _memoryCache[boxName]!.values.cast<TDataEntity>().toList();
    } else {
      // Check database cache
      if (filter != null) {
        if (filter.kind == FilterKind.entityKey && filter is FilterByKey) {
          final entity = box.getBeyKey(filter.key);
          if (entity != null) {
            cachedEntities = [entity];
          }
        } else if (filter.kind == FilterKind.range && filter is FilterByRange) {
          cachedEntities = box.select(filter.startKey, filter.endKey);
        }
      } else {
        cachedEntities = box.getAll();
      }
    }

    // Determine if we need to fetch from API
    bool needToFetch = false;
    if (fetchDataPolicy == FetchDataPolicy.always) {
      needToFetch = true;
    } else if (fetchDataPolicy == FetchDataPolicy.onEmptyCache && cachedEntities.isEmpty) {
      needToFetch = true;
    }

    if (needToFetch) {
      final apiResult = await fetchFromApi();
      if (apiResult.isSuccess) {
        final newEntities = transformToDataEntities(apiResult.result!);

        if (cacheUpdateMode == CacheUpdateMode.box) {
          box.deleteBox();
          if (useMemoryCache) {
            _memoryCache.remove(boxName);
          }
        } else if (cacheUpdateMode == CacheUpdateMode.entities) {
          if (filter != null) {
            List<TDataEntity> entitiesToDelete = [];
            if (filter.kind == FilterKind.entityKey && filter is FilterByKey) {
              final entity = box.getBeyKey(filter.key);
              if (entity != null) {
                entitiesToDelete.add(entity);
              }
            } else if (filter.kind == FilterKind.range && filter is FilterByRange) {
              entitiesToDelete = box.select(filter.startKey, filter.endKey);
            }
            box.deleteEntities(entitiesToDelete);
            if (useMemoryCache) {
              for (final entity in entitiesToDelete) {
                _memoryCache[boxName]?.remove(entity.key);
              }
            }
          }
        }

        box.putAll(newEntities);
        if (useMemoryCache) {
          if (!_memoryCache.containsKey(boxName)) {
            _memoryCache[boxName] = {};
          }
          for (final entity in newEntities) {
            _memoryCache[boxName]![entity.key] = entity;
          }
        }

        cachedEntities = newEntities;
      } else {
        return ResultOrError.fromError(apiResult.error!);
      }
    }

    final models = cachedEntities.map(transformToModel).toList();
    return ResultOrError.fromResult(models);
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    if (useMemoryCache && _memoryCache.containsKey(boxName)) {
      final entity = _memoryCache[boxName]![key] as TDataEntity?;
      if (entity != null) {
        return transformToModel(entity);
      }
    }

    final box = ObjectDdMock<TDataEntity>();
    final entity = box.getBeyKey(key);
    if (entity != null) {
      return transformToModel(entity);
    }

    return null;
  }
}

void main() async {
  final cacheManager = ObjectDdCacheManager(
      fetchFromApi: () async {
        return ResultOrError.fromResult(ApiObject1());
      },
      transformToDataEntities: (apiObject) {
        return apiObject.items.keys
            .map((k) => DataEntity1(k, apiObject.items[k]!))
            .toList();
      },
      transformToModel: (dataEntity) {
        return Model1(dataEntity.value);
      },
      boxName: 'box1');

  final items = await cacheManager.retrieveItems();

  print(items.result!.length);
}
Отредактировано 16.01.2025 7:06 Lepsik . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.